title: 爬虫日记-豆瓣新书速递
tags: ['requests','python','lxml','openpyxl']
date: 2021-12-07
categories: "爬虫日记"
环境
window10
python3
使用到的库
re 字符串匹配
openpyxl 表格操作
requests 网络请求
lxml 网页源码解析
步骤
写爬虫之前首先要知道我要怎么写一个爬虫,就像如果要把大象塞进冰箱,也同样思考步骤一样。
由上图的类比,我们可以有条不紊,步骤清晰的开始写爬虫了。
1 找到数据
首先我们奥找到我们要采集的数据。
1.1 url
这次我们采集的是豆瓣读书的新书榜,也就是 https://book.douban.com/latest 这个网址。设定目标为新书速递10页的全部数据,那第一页的网址我们就已经拿到了,也就是 https://book.douban.com/latest 。
页面拉到最下面,点一下后面一页的页码,进入第二页,发现它的网址发生了一些变化,如下:
首页之后的网址多了 ?subcat=全部&p=3
,结合首页的url,进行合理的推理猜测:
`latest`为最新的意思,即新书速递中的新的意思;
`subcat`大概是 `sub catalogue`,即子目录,`=`后边的全部对应着新书速递页面下面的分类:
![image](https://upload-images.jianshu.io/upload_images/18578921-c896880f7fff46f1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
猜测首页的url也可以用后面的格式写,即首页的url为:
![image](https://upload-images.jianshu.io/upload_images/18578921-681c956de7f99aaa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
通过测试,首页的确可以通过这样的方式访问得到。
这里实际的网址为:`https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=1`,中文的全部在url传递时候会需要进行编码,这里不展开。
综上,这10页的url地址为:
```python
url_list = []
for i in range(1,11):
url_list.append("https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p={0}".format(i))
# [
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=1',
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=2',
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=3',
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=4',
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=5',
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=6',
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=7',
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=8',
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=9',
# 'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=10'
# ]
```
1.2 每页具体要拿到的数据
找完了url,接着我们只要搞定其中的一页,那么其他的都一个样子搞就可以了。
网页上显示的元素有:
书名、书籍信息、评分、评价人数。
同样的,每本书要采集的数据都差不多,只要确定写好一个,其他的书也都一样的方式可以拿到。
重复的事情让程序帮我们去做就行了,我们告诉程序怎么去拿就可以。
2 把数据下载下来
2.1 request 请求获取源码
本次采集的数据实在是网页web上的,通过右键网页空白处,查看网页源码,可以看到一堆网页代码,稍微往下拉会发现刚才看到的数据在源码中同样存在着(废话)
所以我们要把网页的源码下载下来。
这里使用python
的 requests
库。
获取源码的程序为:
import requests # 导包
url = "https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=1" # 请求url
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36', # 伪造UA
}
response = requests.request("GET", url, headers=headers) # requests 请求获取源码
print(response.text) # 打印出来看一看
实际运行结果为:
可以看到我们拿到了源码。
2.2 解析源码,获取有用信息
源码已经拿到了,需要的数据也在里面,但是就好像我的1000颗玻璃球放在了一个垃圾场里,我能看得到,也能捡起来,但是好麻烦啊。
因此我们需要想办法让源码变得更条理清晰,便于捡玻璃球。
这里用到的是 lxml
中 etree
,将字符串形式的html源码转换为Element对象,之后用xpath
进行解析。这里先不做展开,下个爬虫再说。
import re
import requests
from lxml import etree
url = "https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=1"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
}
response = requests.request("GET", url, headers=headers)
print(response.text)
_ = etree.HTML(response.text)
data_list = _.xpath("//li[contains(@class,'media clearfix')]")
for data in data_list:
print("-" * 100)
book_name = data.xpath(".//h2/a/text()")[0].strip()
book_info = data.xpath(".//p[@class='subject-abstract color-gray']/text()")[0].strip()
score = data.xpath(".//p[@class='clearfix w250']/span[2]/text()")[0].strip()
score_numbers = data.xpath(".//p[@class='clearfix w250']/span[3]/text()")[0]
score_numbers = re.findall("\d+",score_numbers)[0]
print("书名:\t", book_name)
print("书籍信息:\t", book_info)
print("评分:\t", score)
print("评分人数:\t", score_numbers)
运行结果:
这样我们就拿到了需要的数据。
3 把数据存起来
只是这样把数据都放在黑框框中展示肯定是不行的,还是要找个办法把采集到的数据存起来,这里选择保存到excel中,使用的模块是openpyxl
。
3.1 创建新的工作簿获取当前激活的工作表
from openpyxl import Workbook # 导包
wb = Workbook() # 创建新的工作簿
ws = wb.active # 获取当前激活的工作表,默认第一个啦
3.2 写表头,写数据
ws.cell(1,1).value = "书名"
ws.cell(1,2).value = "书籍信息"
ws.cell(1,3).value = "评分"
ws.cell(1,4).value = "评分人数"
for index,data in enumerate(data_list):
......
print("书名:\t", book_name)
print("书籍信息:\t", book_info)
print("评分:\t", score)
print("评分人数:\t", score_numbers)
ws.cell(2+index,1).value = book_name
ws.cell(2+index,2).value = book_info
ws.cell(2+index,3).value = score
ws.cell(2+index,4).value = score_numbers
3.3 保存表格
import re
from openpyxl import Workbook
import requests
from lxml import etree
url = "https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=1"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
}
response = requests.request("GET", url, headers=headers)
print(response.text)
_ = etree.HTML(response.text)
wb = Workbook()
ws = wb.active
ws.cell(1,1).value = "书名"
ws.cell(1,2).value = "书籍信息"
ws.cell(1,3).value = "评分"
ws.cell(1,4).value = "评分人数"
data_list = _.xpath("//li[contains(@class,'media clearfix')]")
for index,data in enumerate(data_list):
print("-" * 100)
book_name = data.xpath(".//h2/a/text()")[0].strip()
book_info = data.xpath(".//p[@class='subject-abstract color-gray']/text()")[0].strip()
score = data.xpath(".//p[@class='clearfix w250']/span[2]/text()")[0].strip()
score_numbers = data.xpath(".//p[@class='clearfix w250']/span[3]/text()")[0]
score_numbers = re.findall("\d+",score_numbers)[0]
print("书名:\t", book_name)
print("书籍信息:\t", book_info)
print("评分:\t", score)
print("评分人数:\t", score_numbers)
ws.cell(2+index,1).value = book_name
ws.cell(2+index,2).value = book_info
ws.cell(2+index,3).value = score
ws.cell(2+index,4).value = score_numbers
wb.save('./新书速递.xlsx')
执行程序会在当前文件夹下生成新书速递.xlsx
文件,文件内容为
到目前为止,可以拿到第一页我们所需要的信息,但是后面的每一页都要按照这样的方式重新操作一边,这样肯定是太麻烦了,程序到现在能跑,但是还是可以优化一下。
4总结优化
4.1 梳理程序流程
程序整体流程如下
其中,访问网页,保存数据都是多次执行的操作,可以把它写成函数,需要执行的时候调用即可。
4.2 优化
直接上程序
import re
from openpyxl import Workbook
import requests
from lxml import etree
def get_source_code(url):
"""
获取给定url的网页源码
:param url: 要去访问的url
:return:返回该yrl网页源码,字符串形式
"""
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
}
response = requests.request("GET", url, headers=headers)
return response.text
def get_info(text):
"""
从字符串的网页源码中获取所需要的信息
:param text: 字符串形式的网页源码
:return: 列表形式的所需要的信息
[
[book_name1,book_info1,score1,score_numbers1],
[book_name2,book_info2,score2,score_numbers2],
[book_name3,book_info3,score3,score_numbers3],
...
]
"""
_ = etree.HTML(text)
data_list = _.xpath("//li[contains(@class,'media clearfix')]")
res = []
for index, data in enumerate(data_list):
print("-" * 100)
book_name = data.xpath(".//h2/a/text()")[0].strip()
book_info = data.xpath(".//p[@class='subject-abstract color-gray']/text()")[0].strip()
score = data.xpath(".//p[@class='clearfix w250']/span[2]/text()")[0].strip()
score_numbers = data.xpath(".//p[@class='clearfix w250']/span[3]/text()")[0]
score_numbers = re.findall("\d+", score_numbers)[0]
res.append([book_name, book_info, score, score_numbers])
return res
def write_to_excel(data_list, path="./新书速递.xlsx"):
"""
将数据写入到同级目录下的execl表格中。
:param data_list: 要写入的数据
:param path: 写入的地址,这地方写死,同级目录下
:return: 输出一个名为 新书速递 的表格文件到本地统计目录下
"""
wb = Workbook()
ws = wb.active
# 写表头
ws.cell(1, 1).value = "书名"
ws.cell(1, 2).value = "书籍信息"
ws.cell(1, 3).value = "评分"
ws.cell(1, 4).value = "评分人数"
for index, data in enumerate(data_list):
ws.cell(2 + index, 1).value = data[0]
ws.cell(2 + index, 2).value = data[1]
ws.cell(2 + index, 3).value = data[2]
ws.cell(2 + index, 4).value = data[3]
wb.save(path)
if __name__ == '__main__':
data_list = []
for i in range(1, 11):
# 获取网址
url = "https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p={0}".format(i)
# 获取网页源码
text = get_source_code(url)
# 获取所需信息,并保存在程序中
data_list += get_info(text)
# 写入本地
write_to_excel(data_list)
写在最后
不管是写些什么,开始写之前一定要做好规划。正所谓磨刀不误砍柴工,不管是思考一下流程,还是确定框架,设计模式,都要清楚自己写的每一行代码到底是干嘛的。
这个爬虫实际上还是存在很多问题的,但是我想用这个爬虫来记录一下整个写程序的思路。如果可以,从最后的图片对照着mian
函数开始看,一定要清楚每一行代码究竟是在整个程序中扮演者什么角色,执行哪一步操作。
实力有限,才疏学浅,如有错误,欢迎指正。
- 我的个人博客 菜猫子小六 - 博客 (codesix.site)
- 我的简书 菜猫子小六 - 简书 (jianshu.com)
- 我的CSDN 菜猫子小六 - CSDN