分析Ajax爬取今日头条街拍美图-崔庆才思路
站点分析
首先,打开头条,在搜索框输入关键字之后,在返回的页面中,勾选Perserve log,这玩意儿在页面发生变化的时候,不会清除之前的交互信息.
在返回的response中,我们看不到常见的HTML代码,所以初步判定,这个网站是通过ajax动态加载的.

切换到XHR过滤器,进一步查看.

发现随着网页的滚动,会产生类似这样的的Ajax请求出来. 仔细查看内容,可以看到与网页中条目对应的title和article_url.
所以初步思路,通过article_url字段先抓取文章条目

分析json数据,可以看到,这里有article_url,另外,这次要抓取的是图集形式的页面,所以要注意下这个has_gallery
然后我们再来看具体的页面
在具体页面的html中,我们发现,图片的所有链接直接在网页源代码中包含了,所以,我们直接拿到源码,正则匹配一下就好了.

至此,页面分析完成.
开工!
源码及遇到的问题
代码结构
方法定义
def get_page_index(offset, keyword): 获取搜索结果索引页面
def parse_page_index(html): 解析索引页面,主要是解析json内容,所以需要用到json.loads方法
def get_page_detail(url): 用来获取具体图片的页面,与索引页获取差不多
def parse_page_details(html, url):解析具体图集页面
def save_to_mongo(result): 将标题,url等内容保存到mongoDB数据库. 之所以使用mongoDB数据库,因为mongoDB简单,而且是K-V方式的存储,对于字典类型很友好
def download_image(url): 下载图片
def save_img(content): 保存图片
def main(offset): 对以上各种方法的调用
需要的常量
MONGO_URL = 'localhost' # 数据库位置
MONGO_DB = 'toutiao' # 数据库名
MONGO_TABLE = 'toutiao'# 表名
GROUP_START = 1 # 循环起始值
GROUP_END = 20 # 循环结束值
KEY_WORD = '街拍' # 搜索关键字
关于在代码中遇到的问题
01. 数据库连接
第一次在python中使用数据库,而且用的还是MongoDB. 使用之前引入 pymongo库,数据库连接的写法比较简单. 传入url 然后在创建的client中直接指定数据库名称就可以了.
client = pymongo.MongoClient(MONGO_URL,connect=False)
db = client[MONGO_DB]
02.今日头条的反爬虫机制
今日头条比较有意思,反爬虫机制不是直接给个400的回应,而是返回一些错误的 无效的代码或者json. 不明白是什么原理,是请求不对,还是怎么了. 所以针对今日头条的反爬虫机制,经过尝试之后发现需要构造get的参数和请求头.
而且今日头条的请求头中,需要带上cookie信息. 不然返回的response还是有问题.
这里还要注意的就是cookie信息有时效问题,具体多长时间,我也没搞明白,几个小时应该是有的,所以在执行之前,cookie最好更新一下
同样的在获取详情页的时候也有这个问题存在. 而且还犯了一个被自己蠢哭的错误. headers没有传到requests方法中去.
def get_page_index(offset, keyword):
timestamp = int(time.time())
data = {
"aid": "24",
"app_name": "web_search",
"offset": offset,
"format": "json",
"keyword": keyword,
"autoload": "true",
"count": "20",
"en_qc": "1",
"cur_tab": "1",
"from": "search_tab",
# "pd": "synthesis",
"timestamp": timestamp
}
headers = {
# 这里小心cookie失效的问题
'cookie': 'tt_webid=6791640396613223949; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6791640396613223949; csrftoken=4a29b1b1d9ecf8b5168f1955d2110f16; s_v_web_id=k6g11cxe_fWBnSuA7_RBx3_4Mo4_9a9z_XNI0WS8B9Fja; ttcid=3fdf0861117e48ac8b18940a5704991216; tt_scid=8Z.7-06X5KIZrlZF0PA9kgiudolF2L5j9bu9g6Pdm.4zcvNjlzQ1enH8qMQkYW8w9feb; __tasessionId=ngww6x1t11581323903383',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36'}
url = 'https://www.toutiao.com/api/search/content/?' + urlencode(data)
response = requests.get(url, headers=headers)
try:
if response.status_code == 200:
return response.text
return None
except RequestException:
print('Request failed!')
return None
03. json解码遇到的问题
由于python和java转移字符的区别(python通过''进行转义,''本身不需要转义),但是java需要\\来进行转义,也就是''本身还需要一个''来进行转义.
但是python的json.loads()方法和print方法在输出的时候都会对转义字符进行解释.
所以当初在parse_page_details()这个方法中 json.loads()报错,说json格式错误找不到'"'. 但是print出来的时候,又是一个''的样子.
后来在在debug的时候,看到了真实的json字符串的样子

所以就需要对这个json字符串进行预处理,然后再使用json.loads()进行解码.
eval(repr(result.group(1)).replace('\\\\', '\\'))
插一个小话题,那就是str()方法和repr()方法的区别. 首先两者都是把对象转换成字符串,而无论print方法还是str()方法调用的都是类中的__str__ 而repr()方法调用的是__repr__ .
简单来说,__str__方法是为了满足可读性,会对输出内容做可读性处理. 比如去掉字符串两端的引号或者自动解析''等. 但是__repr__会尽量保留原始数据格式,满足的是准确性需求. 所以这里,我们使用repr()方法拿到原始数据,然后将\\ 替换为\
ps.\\\\ 是两个\ 转义了一下. 同理两个斜杠是一个斜杠,因为也是转义的.
然后就是eval方法是能把字符串转换成对应的类型.
#字符串转换成列表
>>>a = "[[1,2], [3,4], [5,6], [7,8], [9,0]]"
>>>type(a)
<type 'str'>
>>> b = eval(a)
>>> print b
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]]
>>> type(b)
<type 'list'>
#字符串转换成字典
>>> a = "{1: 'a', 2: 'b'}"
>>> type(a)<type 'str'
>>>> b = eval(a)
>>> print b
{1: 'a', 2: 'b'}>>> type(b)<type 'dict'>
理解repr()和eval()两个方法之后,那上面的预处理代码就好理解了,先通过repr()方法获取原始字符串,然后替换,然后再给他转换成可读的字符串. 然后在用json.loads()解码.
04. 关于response.text和response.content的区别
response.text 获取文本值
response.content 获取二进制内容
源代码
import json
import os
import re
from hashlib import md5
from multiprocessing import Pool
from urllib.parse import urlencode
import pymongo
import requests
from bs4 import BeautifulSoup
from requests.exceptions import RequestException
from config import *
# mongodb 数据库对象
# connext=False表示进程启动的时候才进行连接
client = pymongo.MongoClient(MONGO_URL,connect=False)
db = client[MONGO_DB]
def get_page_index(offset, keyword):
data = {
"aid": "24",
"app_name": "web_search",
"offset": offset,
"format": "json",
"keyword": keyword,
"autoload": "true",
"count": "20",
"en_qc": "1",
"cur_tab": "1",
"from": "search_tab",
# "pd": "synthesis",
# "timestamp": "1581315480994"
}
headers = {
# 这里小心cookie失效的问题
'cookie': 'tt_webid=6791640396613223949; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6791640396613223949; csrftoken=4a29b1b1d9ecf8b5168f1955d2110f16; s_v_web_id=k6g11cxe_fWBnSuA7_RBx3_4Mo4_9a9z_XNI0WS8B9Fja; ttcid=3fdf0861117e48ac8b18940a5704991216; tt_scid=8Z.7-06X5KIZrlZF0PA9kgiudolF2L5j9bu9g6Pdm.4zcvNjlzQ1enH8qMQkYW8w9feb; __tasessionId=ngww6x1t11581323903383',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36'}
url = 'https://www.toutiao.com/api/search/content/?' + urlencode(data)
response = requests.get(url, headers=headers)
try:
if response.status_code == 200:
return response.text
return None
except RequestException:
print('Request failed!')
return None
def parse_page_index(html):
data = json.loads(html)
# json.loads()方法会格式化结果,并生成一个字典类型
# print(data)
# print(type(data))
try:
if data and 'data' in data.keys():
for item in data.get('data'):
if item.get('has_gallery'):
yield item.get('article_url')
except TypeError:
pass
def get_page_detail(url):
headers = {
'cookie': 'tt_webid=6791640396613223949; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6791640396613223949; csrftoken=4a29b1b1d9ecf8b5168f1955d2110f16; s_v_web_id=k6g11cxe_fWBnSuA7_RBx3_4Mo4_9a9z_XNI0WS8B9Fja; ttcid=3fdf0861117e48ac8b18940a5704991216; tt_scid=8Z.7-06X5KIZrlZF0PA9kgiudolF2L5j9bu9g6Pdm.4zcvNjlzQ1enH8qMQkYW8w9feb; __tasessionId=yix51k4j41581315307695',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36',
# ':scheme': 'https',
# 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
# 'accept-encoding': 'gzip, deflate, br',
# 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7'
}
try:
# 他妈的被自己蠢哭...忘了写headers了,搞了一个多小时
response = requests.get(url, headers=headers)
# print(response.status_code)
if response.status_code == 200:
return response.text
return None
except RequestException:
print("请求详情页出错!")
return None
def parse_page_details(html, url):
soup = BeautifulSoup(html, 'xml')
title = soup.select('title')[0].get_text()
# print(title)
img_pattern = re.compile('JSON.parse\("(.*?)"\),', re.S)
result = re.search(img_pattern, html)
if result:
# 这里注意一下双斜杠的问题
data = json.loads(eval(repr(result.group(1)).replace('\\\\', '\\')))
if data and 'sub_images' in data.keys():
sub_images = data.get('sub_images')
images = [item.get('url') for item in sub_images]
for image in images: download_image(image)
return {
'title': title,
'url': url,
'images': images
}
def save_to_mongo(result):
if db[MONGO_TABLE].insert_one(result):
print('存储到MongoDB成功', result)
return True
return False
def download_image(url):
print('正在下载', url)
try:
response = requests.get(url)
if response.status_code == 200:
save_img(response.content)
return None
except RequestException:
print('请求图片出错', url)
return None
def save_img(content):
file_path = '{0}/img_download/{1}.{2}'.format(os.getcwd(), md5(content).hexdigest(), 'jpg')
if not os.path.exists(file_path):
with open(file_path, 'wb') as f:
f.write(content)
f.close()
def main(offset):
html = get_page_index(offset, KEY_WORD)
for url in parse_page_index(html):
html = get_page_detail(url)
if html:
result = parse_page_details(html, url)
if result: save_to_mongo(result)
if __name__ == '__main__':
groups = [x * 20 for x in range(GROUP_START, GROUP_END + 1)]
pool = Pool()
pool.map(main, groups)
分析Ajax爬取今日头条街拍美图-崔庆才思路的更多相关文章
- 【Python3网络爬虫开发实战】 分析Ajax爬取今日头条街拍美图
前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:haoxuan10 本节中,我们以今日头条为例来尝试通过分析Ajax请求 ...
- 【Python3网络爬虫开发实战】6.4-分析Ajax爬取今日头条街拍美图【华为云技术分享】
[摘要] 本节中,我们以今日头条为例来尝试通过分析Ajax请求来抓取网页数据的方法.这次要抓取的目标是今日头条的街拍美图,抓取完成之后,将每组图片分文件夹下载到本地并保存下来. 1. 准备工作 在本节 ...
- 转:【Python3网络爬虫开发实战】6.4-分析Ajax爬取今日头条街拍美图
[摘要] 本节中,我们以今日头条为例来尝试通过分析Ajax请求来抓取网页数据的方法.这次要抓取的目标是今日头条的街拍美图,抓取完成之后,将每组图片分文件夹下载到本地并保存下来. 1. 准备工作 在本节 ...
- 分析Ajax抓取今日头条街拍美图
spider.py # -*- coding:utf-8 -*- from urllib import urlencode import requests from requests.exceptio ...
- 分析Ajax来爬取今日头条街拍美图并保存到MongDB
前提:.需要安装MongDB 注:因今日投票网页发生变更,如下代码不保证能正常使用 #!/usr/bin/env python #-*- coding: utf-8 -*- import json i ...
- 关于爬虫的日常复习(9)—— 实战:分析Ajax抓取今日头条接拍美图
- python爬虫之分析Ajax请求抓取抓取今日头条街拍美图(七)
python爬虫之分析Ajax请求抓取抓取今日头条街拍美图 一.分析网站 1.进入浏览器,搜索今日头条,在搜索栏搜索街拍,然后选择图集这一栏. 2.按F12打开开发者工具,刷新网页,这时网页回弹到综合 ...
- 15-分析Ajax请求并抓取今日头条街拍美图
流程框架: 抓取索引页内容:利用requests请求目标站点,得到索引网页HTML代码,返回结果. 抓取详情页内容:解析返回结果,得到详情页的链接,并进一步抓取详情页的信息. 下载图片与保存数据库:将 ...
- Python Spider 抓取今日头条街拍美图
""" 抓取今日头条街拍美图 """ import os import time import requests from hashlib ...
随机推荐
- 2013 ACM-ICPC亚洲区域赛南京站C题 题解 轮廓线DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4804 题目大意 给你一个 \(n \times m\) 的矩形区域.你需要用 \(1 \times 1 ...
- 1026 程序运行时间 (15 分)C语言
题目描述 要获得一个C语言程序的运行时间,常用的方法是调用头文件time.h,其中提供了clock()函数,可以捕捉从程序开始运行到clock()被调用时所耗费的时间.这个时间单位是clock tic ...
- 钱包开发经验分享:ETH篇
# 钱包开发经验分享:ETH篇 [TOC] ## 开发前的准备 > 工欲善其事,必先利其器 一路开发过来,积累了一些钱包的开发利器和网站,与大家分享一下.这些东西在这行开发过的人都知道,只是给行 ...
- makefile个人理解
makefile makefile抽象层面的理解 学习某一样东西之前一定要明确学习的目的,即学习了这项工具能解决一些什么问题,其优势是什么? makefile的优势就是能够动态根据文件的新旧来决定是否 ...
- Go Web 编程之 响应
概述 上一篇文章中,我们介绍了请求的结构与处理.本文将详细介绍如何响应客户端的请求.其实在前面几篇文章中,我们已经使用过响应的功能--通过http.ResponseWriter发送字符串给客户端. 但 ...
- 【转】Zookeeper原理
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等.Zookeeper是hadoop的一个子项目,其 ...
- 8款极具表现力的jQuery/CSS3网页菜单
上一篇我向大家分享了7款效果震憾的HTML5应用组件,今天主要来分享一下CSS3网页菜单,因为在一个网站中,菜单起着举足轻重的作用,所以作为WEB开发人员,我们有必要将网站的菜单设计得尽量完美,下面向 ...
- Spring循环依赖的解决
## Spring循环依赖的解决 ### 什么是循环依赖 循环依赖,是依赖关系形成了一个圆环.比如:A对象有一个属性B,那么这时候我们称之为A依赖B,如果这时候B对象里面有一个属性A.那么这时候A和B ...
- C#中的结构体和对象区别
经常听到有朋友在讨论C#中的结构与类有什么区别.正好这几日闲来无事,自己总结一下,希望大家指点. 1. 首先是语法定义上的区别啦,这个就不用多说了.定义类使用关键字class 定义结构使用关键字str ...
- UGUI源码之Graphic
Graphic是用来显示图像的一个抽象类,是MaskableGraphic的父类,而MaskableGraphic是Image.RawImage.Text的父类. Graphic继承于UIBehavi ...