前言
先上一波爬取的结果:
实战
引入类库
import requests
from bs4 import BeautifulSoup
from requests.exceptions import RequestException
from urllib.parse import urlencode
import pymongo
import numpy as np
import time
from faker import Faker
分析页面请求
分别请求不同的标签页面,分析请求链接,可以发现如下规律:
tag_url = 'https://book.douban.com' + 标签页中a标签括起来的内容
由此,我们可以构建以下代码,以获取标签页面所有标签链接:
# 解析总标签页面,并拼接获得所有标签页页面链接
def splice_tags_indexhtml(html):
url = 'https://book.douban.com'
book_tags = []
tags_url = []
soup = BeautifulSoup(html, 'lxml')
tagurl_lists = soup.select('#content > div > div.article > div > div > table > tbody > tr > td > a')
for tag_url in tagurl_lists:
# 获取全部标签的a标签内容,并拼接到一起
book_tags += [tag_url.attrs["href"]]
for book_tag in book_tags:
tags_url.append([url + book_tag])
return tags_url
我们进入单个标签页面,分析图书列表页面,解析我们需要存储的字段
我们通过bs4解析我们需要的字段,如:出版时间,作者/译者,豆瓣评分,售价,评价人数等。
# 解析单个tag页面下单页的信息
def parse_tag_page(html):
try:
soup = BeautifulSoup(html,"lxml")
tag_name = soup.select('title')[0].get_text().strip()
list_soup = soup.find('ul', {'class': 'subject-list'})
if list_soup == None:
print('获取信息列表失败')
else:
for book_info in list_soup.findAll('div', {'class': 'info'}):
# 书名
title = book_info.find('a').get('title').strip()
# 评价人数
people_num = book_info.find('span', {'class': 'pl'}).get_text().strip()
# 出版信息,作者
pub = book_info.find('div', {'class': 'pub'}).get_text().strip()
pub_list = pub.split('/')
try:
author_info = '作者/译者: ' + '/'.join(pub_list[0:-3])
except:
author_info = '作者/译者: 暂无'
try:
pub_info = '出版信息: ' + '/'.join(pub_list[-3:-1])
except:
pub_info = '出版信息: 暂无'
try:
price_info = '价格: ' + '/'.join(pub_list[-1:])
except:
price_info = '价格: 暂无'
try:
rating_num= book_info.find('span', {'class': 'rating_nums'}).get_text().strip()
except:
rating_num = '0.0'
book_data = {
'title': title,
'people_num': people_num,
'author_info': author_info,
'pub_info': pub_info,
'price_info': price_info,
'rating_num': rating_num
}
# return book_data
if book_data:
save_to_mongo(book_data,tag_name)
except:
print('解析错误')
return None
到这里,我们已经可以获取到单个tag下单页的图书信息,这个时候我们只需要加入翻页功能就可以实现单个tag下所有图书的信息爬取。
可以看到页面多了start和type两个参数,同时start参数是从0开始并以20的偏移量递增的,我们按照这个规律可以构建一个生成器以生成start参数。
从文章的第一张图,可以看出不同的tag页有不同的数量的图书,那页面数量也不尽相同,这时应该如何构建生成器?
这个时候我们发现所有的tag在第50页之后都请求不出信息了,所以我们只需构建前50页的页面链接即可,第51页显示如下:
# 请求tag下的每个页面
def get_tag_page(tag_url,page):
formdata = {
'start': page,
'type': 'T'
}
url = tag_url[0]+'?'+ urlencode(formdata)
try:
reponse = requests.get(url, headers=headers)
if reponse.status_code == 200:
return reponse.text
return None
except RequestException:
print('请求列表页错误')
return None
反反爬
豆瓣的反爬简单粗暴,直接封IP,为了爬虫的健壮,可以使用代理或者随机Header+随机时延的方式,随机时延可以设置为30到40之间,不过这样大大影响了爬取速率,如果需要快速爬取可以采用代理+多线程+随机Header+随机时延这样就能避过反爬又能快速爬取。
#使用Faker库随机生成虚假header
from faker import Faker
fake = Faker()
headers ={'User-Agent':fake.user_agent()}
留心
写给之后的自己
文章是写完代码后,重新回顾的时候写的,回顾之后发现有很多需要优化的地方,比如异常处理部分经常考虑不周导致在爬取的时候异常中断,不得不重新排查错误。还有爬虫的断点续传的功能应该去了解学习下。
尾言
写给公众号的读者
公众号的资源已经都整合到一篇文章中了,后台回复「资源」即可获取公众号资源合集,失效部分已补在留言部分。
我是讲不了段子,编不了故事的咸鱼,这篇文章是咸鱼的第9篇原创文章,感谢大家的支持。
推荐阅读
咸鱼普拉思
一只咸鱼在编程路上的摸爬滚打,记录摸索中的点点滴滴。