文章目录
- 要爬取的内容
- 一级页面(分类中图书的列表)
- 二级页面(每本书的详情页)
- 本案例中的防封ip小技巧
- 多用几个user-agent(随机抽取)
- 设置间隔时间
- 完整代码
本篇文章为豆瓣读书爬虫的案例,采用了xpath解析式,比较基础,未涉及其他深入的爬虫知识
要爬取的内容
根据豆瓣图书中不同的分类爬取图书的相关信息 ( 每个分类豆瓣最多给50页数据 )
一级页面(分类中图书的列表)
爬取 : 书名(文本和url),作者,出版社,出版日期,评价数,缩略图链接,短简介
二级页面(每本书的详情页)
通过一级页面 书名的url链接进入二级页面
爬取 : 评分,内容简介,作者简介,标签
本案例中的防封ip小技巧
多用几个user-agent(随机抽取)
def header_x():
# 随机获取一个headers
user_agents = ['Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0'
]
headers = {
"User-Agent": random.choice(user_agents)
}
return headers
常见user-agent大全:(转,大佬写的(非本人))
设置间隔时间
设置每条数据的时间间隔
time.sleep(random.randint(5, 8))
设置换页的时间等待时间
time.sleep(random.randint(3, 5))
注意:随机数千万不要从0开始,不要太小,容易被封ip
完整代码
import requests
from lxml import etree
import csv
import re
import time
import random
def header_x():
# 随机获取一个headers
user_agents = ['Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0'
]
headers = {
"User-Agent": random.choice(user_agents)
}
return headers
# 要爬取的 豆瓣读书中的 分类名称
kinds = ['','']
for book_kind in kinds:
# 为每个分类创建一个csv文件
csvFile = open("{}.csv".format(book_kind), mode="w+", encoding="utf-8")
for i in range(1, 51):
print('{name} 开始爬取第 {index} 页'.format(name=book_kind,index=i))
# 拼接url
url = 'https://book.douban.com/tag/{name}?start={num}&type=T'.format(name=book_kind,num=i*20-20)
headers = header_x()
resp = requests.get(url, headers=headers)
html = etree.HTML(resp.text)
lis = html.xpath("//div[@id='subject_list']/ul/li")
for li in lis:
try:
name = li.xpath("./div[@class='info']/h2/a/@title") # 书名
img_url = li.xpath("./div[@class='pic']/a/@href") # 缩略图链接
author = li.xpath("./div[@class='info']/div[@class='pub']/text()")[0].strip().split('/')[0] # 作者
publisher = li.xpath("./div[@class='info']/div[@class='pub']/text()")[0].strip().split('/')[-3] # 出版社
publish_time = li.xpath("./div[@class='info']/div[@class='pub']/text()")[0].strip().split('/')[-2] # 出版年
# 判断出版社,出版年份是否在指定位置 如果不在 则跳过
if(publish_time.find('-')==-1):
continue
grade = li.xpath(".//span[@class='pl']/text()")[0].strip() # 评价数
# 处理grade 提取数字
grade_num = []
grade_num = re.findall("\d+\.?\d*", grade)
intro_1 = li.xpath("./div[@class='info']/p/text()")[0].strip() # 小简介
# 子链接
son_url = li.xpath("./div[@class='info']/h2/a/@href")[0] #子链接
resp_son = requests.get(son_url, headers=headers)
html_son = etree.HTML(resp_son.text)
# 评分
score = html_son.xpath("//strong[@class='ll rating_num ']/text()")[0].strip()
# 简介
intro = ''.join(html_son.xpath("//div[@id='link-report']//div[@class='intro']/p/text()"))
# 作者简介
author_intro = ''.join(html_son.xpath("//div[@class='indent ']//div[@class='']/div/p/text()"))
# 标签
label = html_son.xpath("//div[@id='db-tags-section']//a/text()")
label = ' '.join(label)
except IndexError:
continue
# 把字符串转换成列表
author = list(author.split('&&'))
publisher = list(publisher.split('&&'))
publish_time = list(publish_time.split('&&'))
intro_1 = list(intro_1.split('&&'))
score = list(score.split('&&'))
intro = list(intro.split('&&'))
author_intro = list(author_intro.split('&&'))
label = list(label.split('&&'))
# 把数据放入列表
result = []
result.extend(name)
result.extend(author)
result.extend(img_url)
result.extend(publisher)
result.extend(publish_time)
result.extend(grade_num)
result.extend(intro_1)
result.extend(score)
result.extend(intro)
result.extend(author_intro)
result.extend(label)
# 将列表写入 csv文件
write = csv.writer(csvFile)
write.writerow(result)
# 设置每条数据的时间间隔
time.sleep(random.randint(5, 8))
print("{name} 第 {index} 页爬取完成!!!".format(name=book_kind,index=i))
# 设置换页的时间等待时间
time.sleep(random.randint(3, 5))
注: 修改kinds列表,就可以直接开始爬取数据了,最长2.5个小时爬取完一个分类(50页)
注: 因为豆瓣中每个详情页的结构可能不同(我也很是费解),所以有些值爬出来的可能为空,导致处理时程序报错,小编为了方便,遇到为空的就 try…except 直接跳过当前详情页了