1. 打开google浏览器,输入www.toutiao.com, 搜索街拍。
2.打开开发者选项,network监看加载的xhr, 数据是ajax异步加载的,可以看到preview里面的data数据
3.下拉刷新查看加载的offset,每次加载20条数据,data是json数据,里面的article_url,是图集详情页的url。
4.首先抓取索引页的内容
data数据来自于索引页的请求都里面的query str
1 # 提取索引页的数据
2 def get_page_index(offset, keyword):
3 data = {
4 'offset': offset,
5 'format': 'json',
6 'keyword': keyword,
7 'autoload': 'true',
8 'count': 20,
9 'cur_tab': 1
10 }
11 # 向路由中出入参数, 构建完整url
12 url = 'http://www.toutiao.com/search_content/?' + urlencode(data)
13 try:
14 response = requests.get(url)
15 if response.status_code == 200:
16 return response.text
17 return None
18 except RequestException:
19 print('请求索引页出错')
20 return None
5. 接下来是解析索引页的数据,提取出所需要的详情页的url,索引页的data是json数据,里面的article_url,是图集详情页的url。
1 # 解析索引页的json数据(Preview),并提取详情页url
2 def parse_page_index(html):
3 try:
4 data = json.loads(html)
5 if data and 'data' in data.keys():
6 for item in data.get('data'):
7 # 用生成器的方式,惰性获取详情页的url
8 yield item.get('article_url')
9 except JSONDecodeError:
10 pass
6. 有了详情页的url,接下来就是获取详情页的数据和代码了
1 # 获取详情页面的数据代码
2 def get_page_detail(url):
3 try:
4 response = requests.get(url, timeout=5)
5 if response.status_code == 200:
6 return response.text
7 return None
8 except RequestException:
9 print('请求详情页出错', url)
10 return None
7. 接着就是解析详情页面,并提取title, 和图片url, 详情页代码数据在Doc中查看, 注意提取的是组图,非组图被过滤了.url_list 是指三个地址都是图片的地址,我们只要有一个原始的url就可以了。
以某个详情页为例,在浏览器中输入http://www.toutiao.com/a6473742013958193678/。开发者选项,network, Doc
1 # 解析详情页面并提取title, 和图片url
2 def parse_page_detail(html, url):
3 soup = BeautifulSoup(html, 'lxml')
4 title = soup.select('title')[0].get_text()
5 print(title)
6 images_pattern = re.compile(r'gallery: (.*?),\n')
7 result = re.search(images_pattern, html)
8 if result:
9 # 将json数据转换为python的字典对象
10 data = json.loads(result.group(1))
11 if data and 'sub_images' in data.keys():
12 sub_images = data.get('sub_images')
13 images = [item.get('url') for item in sub_images]
14 for image in images: download_image(image)
# 返回标题,此详情页的url,和图片url列表
15 return {
16 'title': title,
17 'url': url,
18 'images': images
19 }
8. 把解析提取的数据存储到mongodb中,以字典的方式.
先写个mongo的配置文件config.py
1 MONGO_URL = 'localhost'
2 MONGO_DB = 'toutiao'
3 MONGO_TABLE = 'toutiao'
4
5 GROUP_START = 0
6 GROUP_END = 20
7
8 KEYWORD = '街拍'
然后连接本地mongo,存储数据
1 client = pymongo.MongoClient(MONGO_URL, connect=False) # connect=False防止多线程在后台多次连接mongo数据库
2 db = client[MONGO_DB]
3
4 # 把详情页面的url和标题(title)以及组图的地址list保存到mongo中
5 def save_to_mongo(result):
6 if db[MONGO_TABLE].insert(result):
7 print('存储到MongoDB成功', result)
8 return True
9 return False
9. 下载图片
1 # 下载图数据,并保存图片
2 def download_image(url):
3 print('正在下载', url)
4 try:
5 response = requests.get(url)
6 if response.status_code == 200:
7 # response.content 为二进制数据
8 save_image(response.content)
9 return None
10 except RequestException:
11 print('请求图片失败', url)
12 return None
13
14
15 # 保存图片
16 def save_image(content):
17 # 使用md5生成加密名字,同时防止相同的图片重复下载
18 file_path = '{0}/{1}.{2}'.format(os.getcwd(), md5(content).hexdigest(), 'jpg')
19 if not os.path.exists(file_path):
20 with open(file_path, 'wb') as f:
21 f.write(content)
10. 爬虫主函数
1 def main(offset):
2 html = get_page_index(offset, KEYWORD)
3 for url in parse_page_index(html):
4 html = get_page_detail(url)
5 if html:
6 result = parse_page_detail(html, url)
7 if result: save_to_mongo(result)
11. 开启多进程
1 if __name__ == '__main__':
2 groups = [x * 20 for x in range(GROUP_START, GROUP_END)]
3 pool = Pool()
4 pool.map(main, groups)
12. 需要的库函数
1 import requests
2 import re
3 import pymongo
4 from urllib.parse import urlencode
5 from requests.exceptions import RequestException
6 from json.decoder import JSONDecodeError
7 from bs4 import BeautifulSoup
8 import json
9 import os
10 from hashlib import md5
11 from config import *
12 from multiprocessing import Pool
完整代码: https://github.com/huazhicai/Spider/tree/master/jiepai