分析Ajax抓取今日头条街拍美图
时间: 2018-09-13来源:OSCHINA
前景提要
「深度学习福利」大神带你进阶工程师,立即查看>>>
声明: 此篇文章主要是观看静觅教学视频后做的笔记,原教程地址 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
原文出处: https://www.jzfblog.com/detail/66 ,文章的更新编辑以此链接为准。欢迎关注源站文章!

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

热门排行