1、页面分析
首先,我们再今日头条的首页搜索框输入“风景”搜索,如图所示:
打开开发者模式,刷新页面,查看第一个请求的URL,https://www.toutiao.com/search/?keyword=%E9%A3%8E%E6%99%AF,查看选项卡Response,都是一些JavaScript代码,并没有我们需要的内容,可以初步判定是由Ajax加载,然后用JavaScript渲染,切换到XHR过滤选项卡,找到第一条结果,Preview中查看到一个
title
字段,它正好是第一个数据的标题。
在
data
字段中,有一项image_list
字段,展开后发现是一些url链接,我们打开它发现只是一些缩略图,并不是我们想要的。
所以,我们只能通过获取数据的URL链接,跳转到图片所在的真实页面获取图片的url。
我们发现每一条数据中都有一个
article_url
字段,它就是这条数据的url,打开后可以看多所有图片。
查看网页的源代码,发现都已一些js代码。但其中有一个
articleInfo
字段,可以看到所有图片的url链接。
我们可以通过正则表达式,将所有的url提取出来。
content = re.search('articleInfo:.*?content:(.*?)groupId', html, re.S)
if content:
pattern = re.compile('(http://.*?)&', re.S)
image_urls = re.findall(pattern, content.group(1))
for url in image_urls:
yield url
但是今日头条的各条数据加载情况很多,其中还有一种情况如下:
查看其网页源代码,发现有一组json数据,指定了图片的url,width,height。
这里依然用正则表达式将url提取出来。
pattern = re.compile('url_list(.*?),', re.S)
contents = re.findall(pattern, html)
for content in contents:
#利用sub方法将content字符串第15至倒数第2的值中的\\替换掉
yield re.sub(r'\\', '', content[15:-2])
运行结果:
其中有部分数据并没有进行抓取,比如含视频的,代码还有需要完善的地方。
附上源代码:
import requests, re, os
from hashlib import md5
from multiprocessing import Pool
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
}
#获取页面源代码
def get_page(offset):
params = {
'offset': offset,
'format': 'json',
'keyword': '风景',
'autoload': True,
'count': 20,
'cur_tab': 1,
'from': 'search_tab'
}
url = 'https://www.toutiao.com/search_content/?'
try:
response = requests.get(url=url, params=params, headers=headers)
if response.status_code == 200:
return response.json()
except requests.ConnectionError as e:
print('Error', e.args)
return None
#获取文章标题和链接
def get_image_url(json):
if json.get('data'):
for item in json.get('data'):
if item.get('article_url'):
yield {
'title': item.get('title'),
'article_url': item.get('article_url')
}
#获取图片链接
def get_images(image_url):
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36',
}
response = requests.get(image_url, headers=headers)
if response.status_code == 200:
html = response.text
if 'articleInfo' in html:
content = re.search('articleInfo:.*?content:(.*?)groupId', html, re.S)
if content:
pattern = re.compile('(http://.*?)&', re.S)
image_urls = re.findall(pattern, content.group(1))
for url in image_urls:
#print(url)
yield url
elif 'galleryInfo' in html:
pattern = re.compile('url_list(.*?),', re.S)
contents = re.findall(pattern, html)
for content in contents:
#print(re.sub(r'\\', '', content[15:-2]))
yield re.sub(r'\\', '', content[15:-2])
except requests.ConnectionError as e:
print('ERROR:', e.args)
#下载图片
def save_image(item):
if not os.path.exists(item.get('title')):
os.mkdir(item.get('title'))
try:
for im in get_images(item.get('article_url')):
response = requests.get(im)
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('已下载', file_path)
except requests.ConnectionError as e:
print('Failed to Save Image')
def main(offset):
json = get_page(offset)
for item in get_image_url(json):
print(item)
save_image(item)
GROUP_START = 1
GROUP_END = 5
if __name__ == '__main__':
pool = Pool()
groups = ([x * 20 for x in range(GROUP_START, GROUP_END+1)])
pool.map(main, groups)
pool.close()
pool.join()