爬虫之今日头条街拍



前面的爬虫都是静态页面,遇到动态页面该如何爬取,当时困惑了好久,不知道如何下手,参考了几篇其他大佬的文章,才慢慢有一点懂。

这次的网页是动态加载的今日头条街拍图集网页。看了崔大大的教程,自己动手码一下代码,熟悉一下动态页面的爬虫步骤。

动态页面肯定不能像静态页面一样直接取数据,因为它的数据都是通过 js 渲染进来的,因此先找到对应的数据 js 文件。

首先,先来规划下步骤:

1.观察 js 请求,查看数据通过哪个文件传输。

2.对 js 文件进行请求,获得信息。

3.提取数据,对数据进行处理。


查看 js 请求

打开今日头条,搜索框内输入关键词“街拍”,跳转到街拍页面,往下拉,能看到图片一直在加载新的,而网址没有改变,动态的没错了,打开 F12 ,网页继续往下拉,Network 下出现了新的请求,并且这些请求构造都差不多,随便点击一个请求,打开 Preview ,里面是 json 格式的,其中有图片标题,也有图片的 url 信息,看到里面就是想要采集的数据,要找的就是它。

请求方式为 GET , 再来看下 Form Data ,有 offset , format , keyword , autoload , count , cur_tab , from 这几个参数,offset 是偏移量,也就是一共刷新出来的图片数量,keyword 是关键词, count 是每一页刷新出来的图片数量, 其它参数没什么重要意义,构造网址时直接加上去就行了。

对js进行请求

想要对 js 进行请求,需要先对网址进行构造。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from urllib.parse import urlencode
import requests
import json
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36'
}

# Form Data 中的参数
params = {
'offset': page,
'format': 'json',
'keyword': '街拍',
'autoload': 'true',
'count': '20',
'cur_tab': '3',
'from': 'gallery'
}
# 对网址进行构造, 使用urlencode方法
url = 'https://www.toutiao.com/search_content/?' + urlencode(params)

构造完毕,对网址进行请求。

1
2
3
4
# 对网址进行请求,获取到的json格式
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.json()

提取信息

经过上一步对 js 进行请求后,得到和之前看到的 Preview 里面一样的信息,开始对数据进行提取处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
json = response.json()
# 判断json的data,如果存在继续下一步,其实这里用.get方法更好,因为.get方法如果获取一个不存在的属性时不会报错,而直接使用['']这种方法,如果不存在此属性,就会直接报错,影响程序效率
if json['data']:
# data是个列表,对列表中数据继续遍历
for item in json['data']:
# 获取到title信息和图片地址信息
title = item.get('title')
image_urls = item.get('image_list')
# 因为有多张图片,因此图片信息被装在一个列表中,需要继续遍历
for image_url in image_urls:
# 这里把图片url中的list换成large,提取大图
image = 'http://' + image_url.get('url').strip('//').replace('list', 'large')
# 返回image和title信息
yield {
'image': image,
'title': title
}

保存数据

保存到本地文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os
from hashlib import md5
# 判断当前文件路径下是否存在以图片的标题命名的文件夹,如果不存在,就新建文件夹
if not os.path.exists(item.get('title')):
os.mkdir(item.get('title'))
try:
# 请求图片的url地址
response = requests.get(item.get('image'))
if response.status_code == 200:
file_path = '{0}/{1}.{2}'.format(item.get('title'), md5(response.content).hexdigest(), 'jpg')
if not os.path.exists(file_path):
with open(file_path, 'wb') as f:
f.write(response.content)
else:
print('已经下载过!')
except requests.ConnectionError:
print('连接失败!')

保存到 MongoDB 数据库

1
2
3
4
5
6
def save_to_mongo(data):
client = pymongo.MongoClient('localhost', 27017)
db = client['jrtt']
collection = db['jrtt_spider']
if collection.insert(data):
print('保存到MongoDB成功')

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import os
import pymongo
import requests
from hashlib import md5
from urllib.parse import urlencode
from multiprocessing import Pool


headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36'
}

def get_page_source(url):
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.json()
except Exception:
print('error')


def get_content(json):
if json['data']:
for item in json['data']:
title = item.get('title')
image_urls = item.get('image_list')
for image_url in image_urls:
image = image_url.get('url').replace('list', 'large')
yield {
'image': image,
'title': title
}


def save_to_file(item):
if not os.path.exists(item.get('title')):
os.mkdir(item.get('title'))
try:
response = requests.get(item.get('image'))
if response.status_code == 200:
file_path = '{0}/{1}.{2}'.format(item.get('title'), md5(response.content).hexdigest(), 'jpg')
if not os.path.exists(file_path):
with open(file_path, 'wb') as f:
f.write(response.content)
else:
print('已经下载过!')
except requests.ConnectionError:
print('连接失败!')


def save_to_mongo(data):
client = pymongo.MongoClient('localhost', 27017)
db = client['jrtt']
collection = db['jrtt_spider']
if collection.insert(data):
print('保存到MongoDB成功')


def main(page):
params = {
'aid': '24', # 新增
'offset': page,
'format': 'json',
'keyword': '街拍',
'autoload': 'true',
'count': '20',
'cur_tab': '3',
'from': 'search_tab',
'pd': 'synthesis' # 新增
}
url = 'https://www.toutiao.com/api/search/content/?' + urlencode(params)
json = get_page_source(url)
items = get_content(json)
for item in items:
# 本地输出
print(item)
# 保存至本地文件夹
save_to_file(item)
# 保存标题和链接到MongoDB数据库
save_to_mongo(item)


if __name__ == '__main__':
START = 1
END = 20
pool = Pool()
groups = ([x * 20 for x in range(START, END+1)])
try:
pool.map(main, groups)
pool.close()
pool.join()
except Exception as e:
print()

-------------本文结束感谢您的阅读-------------

本文标题:爬虫之今日头条街拍

文章作者:Tang

发布时间:2018年04月11日 - 17:04

最后更新:2019年02月08日 - 11:02

原始链接:https://tangx1.com/jrtt_spider/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%