知识点整理:
- 目录:
1.分析目标网页代码结构;
2.代码爬取数据;
3.保存或下载数据。
一、分析网页
我们在头条搜索“街拍”弹的网址https://www.toutiao.com/search/?keyword=街拍
,我们按F12查询源码,但很明显这个页面没有我们东西
因此可以想像这个页面内容可能是用ajax加载,点击XHR,看到这里也有一个请求
而且这个请求页的内容适好就是这个页面图片内容,所以目标爬取索引页地址就是这个
https://www.toutiao.com/search_content/?
,请求这个索引页之后就来到图片详情页https://www.toutiao.com/a6579859429166940680/
, 同样也是F12打开网页源码,而经过查找看到这些图片的地址是直接藏在网页里面,所以我们就可以请求这个页面,然后用正则获取我们详情页的美图地址
二、写代码爬取
这里要注意几个点:
- 1、 请求索引页的时候
https://www.toutiao.com/search_content/?
这个页面是有参数的,
因此需要将这些参数urlencode一下然后拼接到请求地址 - 2、 请求详情页的时候,由于网站做了简单的反爬,需要在请求的时候加入请求头
hearders
,这样才能获取到刚刚内容 - 3、 获取到详情页内容,用正则获取到藏有详情页图片地址的json字符串就这样的格式:
{\"count\":6,\"sub_images\":[{\"url\":\"http:\\/\\/p9.pstatp.com\\/origin\\/pgc-image\\/.......}
,反斜杠\
有转义效果,因此不能直接用json.loads
将字符串转为python对象,直转的话会报错json.decoder.JSONDecodeError
,所以我们可能用正则re.sub()
或内置函数replace()
将字符串替换一下,然后再json.loads()
转换
三、下载保存
这一步比较简单,就是直接insert 到MongoDB和write一下文件就可以了,直接看代码就能理解
附上源码:
- 配置文件---config.py
MONGO_URL='localhost' #MongoDB本地连接
MONGO_DB='toutiao' #数据库名
MONGO_TABLE='toutiao' #表名
GROUP_START=0 #起始
GROUP_END =20 #结束
KEYWORD = '街拍' #搜索关键词
- 执行代码文件---spider.py
# -*- coding: utf-8 -*-
import requests
from requests.exceptions import RequestException
from urllib.parse import urlencode
import json
from bs4 import BeautifulSoup
import re
from config import *
import pymongo
from hashlib import md5
import os
from multiprocessing import Pool
#client = pymongo.MongoClient(MONGO_URL,connect=False)#由于启动多进程爬取,所以与MongoDB数据库连接时可能会报错,就需要加上connect=False,
client = pymongo.MongoClient(MONGO_URL)#连接MongoDB
db = client[MONGO_DB]#创建数据库
def get_page_index(offset,keyword):
data={
'offset': offset,
'format': 'json',
'keyword': keyword,
'autoload': 'true',
'count': '20',
'cur_tab': 3,
'from':'gallery',
}
url='https://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("请求索引页url出错")
return None
def parse_page_index(html):
data = json.loads(html)#将json字符串转换成python对象
if data and 'data'in data.keys():
for item in data.get('data'):
yield item.get('article_url')#将方法变成生成器每次返回一个url
def get_page_detail(url):
headers= {
'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
}
try:
response = requests.get(url,headers=headers)#爬取详情页需要加请求头,不然获取不了数据
if response.status_code==200:
return response.text#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('gallery: JSON.parse\("(.*?)"\),',re.S)#根据数据特点用正则筛选想要的数据
result = re.search(images_pattern,html)
if result:
#print(result.group(1))
page_detail = result.group(1)
page_result = page_detail.replace('\\"','"',10000)#这个替换是为了修正数据格式,这样才能用json.loads,否则会报错json.decoder.JSONDecodeError
last_change = page_result.replace('\\/','/',10000)#这个替换是为了修正详情页图片地址
#print(page_result)
data = json.loads(last_change)
if data and 'sub_images'in data.keys():
sub_images = data.get('sub_images')
images = [item.get('url') for item in sub_images]#将详情页图片url循环生成列表
for image in images: download_image(image)#将详情页图片url循环下载
return {
'title':title,
'url':url,
'images':images
}
def save_to_mongo(result):
if db[MONGO_TABLE].insert(result):#将结果插入到MongoDB
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)##response.content一般来说这个返回文件,图片等二进制内容
return None
except RequestException:
print("下载图片出错",url)
return None
def save_image(content):
file_path = '{0}/{1}/{2}.{3}'.format(os.getcwd(),'download_image', 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)
#print(html)
for url in parse_page_index(html):
print(url)
html = get_page_detail(url)
#print(html)
if html:
result = parse_page_detail(html,url)
#print(result)
save_to_mongo(result)
if __name__ == '__main__':
#main()
groups = [x*20 for x in range(GROUP_START,GROUP_END+1)]
poll = Pool()
poll.map(main,groups)