前一节(https://blog.youkuaiyun.com/weixin_44526949/article/details/86745455)我们通过分析一个电影网站,对该网站的前100的电影的相关信息进行了爬取。这种爬取是基于我们可以直接从网页源代码中看到要爬取的内容,只要写好正则表达式进行提取就可以了。但是,有时一些网站,我们并不能从网页源代码中直接找到我们需要的内容,这是因为,这些内容可能是通过Ajax加载,然后使用js进行渲染得到,这时就需要我们来分析网页请求。那么本期,我们通过分析Ajax来获取今日头条街拍美图。那么和之前一样,在进行爬取之前,首先需要进行目标站点的分析。我们打开要爬取的目标网站(https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D),然后和之前的步骤一样,需要分析网页源代码,通过分析网页源代码,来确定我们需要爬去的内容。整个流程框架分4步:
- 抓取索引页内容:利用requests请求目标站点,得到索引网页的HTML代码,返回结果。
- 抓取详情页内容:解析返回结果,得到详情页的链接,并进一步抓取详情页的信息。
- 下载图片与保存数据库:将图片下载到本地,并把页面信息及图片的url保存到MongoDB。
- 开启循环及多线程:对多页内容遍历,开启多线程提高抓取速度。
那么,接下来来完成这个项目,代码如下:
"""
本项目用来完成今日头条街拍美图
由于本次项目抓取的网页与之前的网页代码样式不同,本次的网页是通过ajax请求与js渲染而成的,无法直接
看到我们所要爬去的资源,此时我们需要分析get请求来对网页进行解析
"""
import requests
import re
import json
import pymongo
from bs4 import BeautifulSoup
from urllib.parse import urlencode
from requests.exceptions import RequestException
from config import *
import os
from hashlib import md5
from multiprocessing import Pool
client = pymongo.MongoClient(MONGO_URL, connect=False)
db = client[MONGO_DB]
def get_page_index(offset, keyword):
data = {
'offset': offset,
'format': 'json',
'keyword': 'keyword',
'autoload': 'true',
'count': '20',
'cur_tab': 3
}
url = 'http://www.toutiao.com/search_content/?' + urlencode(data)
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
return None
except RequestException:
print('请求索引页出错')
return None
def parse_page_index(html):
data = json.loads(html)
if data and 'data' in data.keys():
for item in data.get('data'):
yield item.get('article_url')
def get_page_detail(url):
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
return None
except RequestException:
print('请求详情页出错', url)
return None
def parse_page_detail(html, url):
soup = BeautifulSoup(html, 'lxml')
title = soup.select('title')[0].get_text()
print(title)
images_pattern = re.compile('var gallery = (.*?);', re.S)
result = re.search(images_pattern, html)
if result:
data = json.loads(result.group(1))
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(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_image(response.content)
return None
except RequestException:
print('请求图片出错', url)
return None
def save_image(content):
file_path = '{0}/{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, KEYWORD)
for url in parse_page_index(html):
html = get_page_detail(url)
if html:
result = parse_page_detail(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)
我们需要将爬到的内容存储到MongoDB数据库中,需要另写一个模块,内容如下:
"""
将数据存储到MongoDB中
"""
MONGO_URL = 'localhost'
MONGO_DB = 'toutiao'
MONGO_TABLE = 'toutiao'
GROUP_START = 1
GROUP_END = 20
KEYWORD = '街拍'
以上的代码是一个通用的代码,适合任何一个用ajax加载和js渲染过得让我们无法直接看到所需要的网页内容的爬虫任务,只需要修改其中的参数即可,比如,当我们要爬取的任务是“体育”而不是“街拍”时,只需要更改原始代码中的URL和数据库配置中的相关参数,如KEYWORD='街拍'改为KEYWORD='想要的内容...'即可,只要是通过ajax加载和js渲染过的网页,我们从源代码中,无法直接找到我们所要爬取的内容,这时便可以通过分析get请求来对网页进行解析,进一步来获取我们需要的东西。