分析Ajax抓取今日头条街拍美图

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介: 声明:此篇文章主要是观看静觅教学视频后做的笔记,原教程地址https://cuiqingcai.com/实现流程介绍1.

声明:此篇文章主要是观看静觅教学视频后做的笔记,原教程地址https://cuiqingcai.com/

实现流程介绍

1.抓取索引页内容:利用requests请求目标站点,得到索引网页HTML代码,返回结果

2.抓取详情页内容:解析返回结果,得到详情页的链接,并进一步抓取详情页信息

3.下载图片与保存数据库:将图片下载到本地,并把页面信息及图片URL保存至MongoDB

4.开启循环及多线程:对多页内容遍历,开启多线程提高抓取速度

具体实现

1. 首先访问今日头条网站输入关键字来到索引页,我们需要通过分析网站来拿到进入详细页的url    

    经过观察可以发现每次滑动鼠标滚轮,新的标题链接就会被显示,所以可以发现其后台为Ajax请求,通过浏览器Network选项卡的XHR可以找到Ajax的链接,其为一个json数据,以搜索词街拍为例,其链接地址如下:

https://www.toutiao.com/search_content/?offset=0&format=json&keyword=%E8%A1%97%E6%8B%8D&autoload=true&count=20&cur_tab=1&from=search_tab
2.通过点击查看Query String Parameters中的内容,可以看到一些类似字典的数据,所以这是一会需要通过urlencode来转码拼接成最终访问的地址

offset: 0
format: json
keyword: 街拍
autoload: true
count: 20
cur_tab: 1
from: search_tab

3.随着向下滑动滚动条显示更多的图片索引,会发现刷出了很多新的ajax请求,通过这个我们可以知道我们之后可以通过改变offset参数来获取不同的拿到不同的索引界面,从而获得不同的图集详细页url。开始只需实现一个offset参数的爬取,最后通过进程池Pool来创建实现多进程爬取不同offset参数的URL,加快爬取速度

4.接下来就是分析查找图集详细页的代码,来找到图片的url,这个图片url隐藏的比较深,都在JS代码中,所以不能使用BeautifulSoup和PyQuery来解析了,只能通过正则解析,使用正则解析要注意匹配规则一定要写对。刷新页面后,自己基础比较差,找了好久换了火狐浏览器,又换回谷歌,最后在Network选项卡的Doc发现下面这个链接,而图片地址就藏在gallery: JSON.parse里

https://www.toutiao.com/a6585311263927042573/
5.代码实现

    代码直接进行展示吧,需要的注释我已经写在代码里了,先编辑一个config.py的文件,里面设置了代码中用到的变量

MONGO_URL = 'localhost'
MONGO_DB = 'toutiao'
MONGO_TABLE = 'toutiao'

GROUP_START = 1
GROUP_END = 20
KEYWORD='街拍'
#!/usr/bin/env python
# coding=utf-8

from urllib.parse import urlencode
from requests.exceptions import ConnectionError
from bs4 import BeautifulSoup
from json.decoder import JSONDecodeError
from hashlib import md5
from config import *
from multiprocessing import Pool
import requests
import json
import re
import os
import pymongo

client = pymongo.MongoClient(MONGO_URL, connect=False)
db = client[MONGO_DB]

def get_page_index(url, headers):
    """
        作用:返回页面源码
        url:请求地址
        headers:请求头信息
    """
    try:
        response = requests.get(url, headers=headers)
        # 判断是否访问成功
        if response.status_code == 200:
            return response.text
    except ConnectionError:
        print('Erroe occured')
        return None

def parse_page_index(html):
    """
        作用:解析出标题URL地址
        html:网页源码
    """
    try:
        # 将数据转为json格式
        data = json.loads(html)
        # print(data)

        # 判断data是否为空,以及data字典中是否有data这个键
        if data and 'data' in data.keys():
            for item in data.get('data'):
                if item.get('article_url'):
                    yield item.get('article_url')
    except JSONDecodeError:
        pass

def get_page_detail(url, headers):
    """
        作用:返回标题URL网页源码
        url:标题URL地址
        headers:请求头信息
    """
    try:
        response = requests.get(url, headers=headers)
        # 判断是否访问成功
        if response.status_code == 200:
            return response.text
    except ConnectionError:
        print('Error occured')
        return None

def parse_page_detail(html, url):
    """
        作用:解析标题URL地址的每个图片链接
        html:标题URL网页源码
        url:标题URL地址
    """
    # 利用BeautifulSoup找到title的文本
    soup = BeautifulSoup(html, 'lxml') 
    title = soup.title.text
    # 利用正则找到每个下载图片的地址
    images_pattern = re.compile('gallery: JSON.parse\("(.*)"\)', re.S)
    result = images_pattern.search(html)
    # print(result)
    if result:
        data = json.loads(result.group(1).replace('\\', ''))
        # 提取出sub_images键的键值
        if data and 'sub_images' in data.keys():
            sub_images = data.get('sub_images')
            # 使用列表生成式拿到每个图片URL
            images = [item.get('url') for item in sub_images]
            for image in images: 
                # 下载图片
                download_image(image)
                # 将return的结果保存至MongoDB中
                return {
                    'title': title,
                    'url': url,
                    'images': images
                }

def download_image(url):
    """
        作用:返回图片URL源码
        url:图片URL地址
    """
    print('Downloading', url)
    try:
        response = requests.get(url)
        # 判断是否访问成功
        if response.status_code == 200:
            save_image(response.content)
            return None
    except ConnectionError:
        return None

def save_image(content):
    """
        作用:保存图像文件
        content:图像二进制数据
    """
    # 使用md5加密内容,生成图像名称
    file_path = '{0}/{1}.{2}'.format(os.getcwd(), md5(content).hexdigest(), 'jpg')
    print(file_path)
    # 判断该文件名是否存在
    if not os.path.exists(file_path):
        with open(file_path, 'wb') as f:
            f.write(content)
            f.close()

def save_to_mongo(result):
    """
        作用:保存数据至MongoDB数据库
        result:包括图片标题,请求地址,图像地址
    """
    if db[MONGO_TABLE].insert(result):
        print('Successfully Saved to Mongo', result)
        return True
    return False

def jiepai_Spider(offset):
    """
        作用:整个爬虫调度器
        offset:位置参数
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36'
    }

    data = {
        "offset": offset,
        "format": "json",
        "keyword": "街拍",
        "autoload": "true",
        "count": "20",
        "cur_tab": "1",
        "from": "search_tab"
    }
    # 通过urlencode构造请求URL
    url = 'https://www.toutiao.com/search_content/' + '?' + urlencode(data)

    # 测试url
    # print(url)

    # 获取页面源码
    html = get_page_index(url, headers)

    # 解析HTML,获得链接地址
    for url in parse_page_index(html):
        # print(url)
        # 获得每个链接地址的HTML
        html = get_page_detail(url, headers)
        result = parse_page_detail(html, url)

        # 判断result是否为空,保存至MongoDB数据库中
        if result: 
            save_to_mongo(result)


if __name__ == "__main__":
    # 创建进程池
    pool = Pool()
    groups = ([x * 20 for x in range(GROUP_START, GROUP_END + 1)])
    pool.map(jiepai_Spider, groups)
    pool.close()
    # 等待pool中所有子进程执行完成,必须放在close语句之后
    pool.join()

总结思考

1.在利用正则进行匹配的时候如果原文有‘(“ ”)',这类符号时那么你在进行正则表达式书写的时候应该在前面加'\'。按理应该也可以使用原始字符串r,可是我用完最后在匹配的时候返回的是None,留个疑问

pattern = re.compile('gallery: JSON\.parse\("(.*?)"\),', re.S)

2. db = client[MONGO_DB]这里应该是方括号而不是 ( ),否则无法正常访问数据库

3. 在Google浏览器中找不到图片url,然后使用的是火狐浏览器来回查看

4.完整源码地址:https://github.com/XiaoFei-97/toutiao_Spider-Ajax

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
Web App开发 前端开发 API
ajax-hook+ selenium抓取带参数的 Ajax 数据
ajax-hook+ selenium抓取带参数的 Ajax 数据
95 0
|
Web App开发 前端开发 API
ajax-hook+ selenium抓取带参数的 Ajax 数据
ajax-hook+ selenium抓取带参数的 Ajax 数据
137 0
|
Web App开发 前端开发 数据库
ajax-hook+ selenium抓取带参数的 Ajax 数据
ajax-hook+ selenium抓取带参数的 Ajax 数据
115 0
|
Web App开发 前端开发 Python
分析Ajax爬取B站python视频
B 站真是个神奇的网站。找不到资料了,去 B 站逛一逛,保准有你满意的东西。 前几天写了个爬虫,用 path、re、BeautifulSoup 爬取的 B 站 python 视频,但是这个爬虫有有个缺陷,没能获取视频的图片信息,如果你去尝试你会发现它根本就不在返回的结果里面。
1125 0
|
JavaScript 前端开发
|
存储 JSON 前端开发
分析 ajax 请求并抓取今日头条街拍美图
  首先分析街拍图集的网页请求头部:   在 preview 选项卡我们可以找到 json 文件,分析 data 选项,找到我们要找到的图集地址 article_url:   选中其中一张图片,分析 json 请求,可以找到图片地址在 gallery 一栏:   找到图片地址,接下来我们就可以来写代码了: 1.
1047 0
|
Web App开发 移动开发 前端开发
Ajax、Comet、HTML 5 Web Sockets技术比较分析
九十年代中期,WWW以迅猛之势转眼跻身传播信息的主要渠道之一。浏览器的身影开始无处不在,用户也随之开始适应这种信息传播方式。显然,WWW提 供的应用平台能够赢得历史上任何一个平台都无法比及的用户量。
974 0
|
Web App开发 JSON 前端开发
Jquery .ajax方法分析(一)
jQuery.ajax( options )   有很多选项,介绍其中的几个: ·dataType:想从服务器得到哪种类型的数据。xml,html,script,json,jsonp,text ·success:请求成功后的处理函数 ·type:以POST或GET的方式请求。
777 0