爬虫日记-使用requests采集豆瓣新书速递


title: 爬虫日记-豆瓣新书速递
tags: ['requests','python','lxml','openpyxl']
date: 2021-12-07
categories: "爬虫日记"



环境
window10
python3
使用到的库
re          字符串匹配
openpyxl    表格操作
requests    网络请求
lxml        网页源码解析

源码地址

步骤

写爬虫之前首先要知道我要怎么写一个爬虫,就像如果要把大象塞进冰箱,也同样思考步骤一样。

image

由上图的类比,我们可以有条不紊,步骤清晰的开始写爬虫了。

1 找到数据

首先我们奥找到我们要采集的数据。

1.1 url

这次我们采集的是豆瓣读书的新书榜,也就是 https://book.douban.com/latest 这个网址。设定目标为新书速递10页的全部数据,那第一页的网址我们就已经拿到了,也就是 https://book.douban.com/latest

页面拉到最下面,点一下后面一页的页码,进入第二页,发现它的网址发生了一些变化,如下:

image

首页之后的网址多了 ?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,接着我们只要搞定其中的一页,那么其他的都一个样子搞就可以了。

网页上显示的元素有:

image

书名、书籍信息、评分、评价人数。

同样的,每本书要采集的数据都差不多,只要确定写好一个,其他的书也都一样的方式可以拿到。

重复的事情让程序帮我们去做就行了,我们告诉程序怎么去拿就可以。

2 把数据下载下来

2.1 request 请求获取源码

本次采集的数据实在是网页web上的,通过右键网页空白处,查看网页源码,可以看到一堆网页代码,稍微往下拉会发现刚才看到的数据在源码中同样存在着(废话)

image

所以我们要把网页的源码下载下来。

这里使用pythonrequests 库。

获取源码的程序为:

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)    # 打印出来看一看

实际运行结果为:

image

可以看到我们拿到了源码。

2.2 解析源码,获取有用信息

源码已经拿到了,需要的数据也在里面,但是就好像我的1000颗玻璃球放在了一个垃圾场里,我能看得到,也能捡起来,但是好麻烦啊。

因此我们需要想办法让源码变得更条理清晰,便于捡玻璃球

这里用到的是 lxmletree ,将字符串形式的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)

运行结果:

image

这样我们就拿到了需要的数据。

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文件,文件内容为

image

到目前为止,可以拿到第一页我们所需要的信息,但是后面的每一页都要按照这样的方式重新操作一边,这样肯定是太麻烦了,程序到现在能跑,但是还是可以优化一下。

4总结优化

4.1 梳理程序流程

程序整体流程如下

image

其中,访问网页,保存数据都是多次执行的操作,可以把它写成函数,需要执行的时候调用即可。

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函数开始看,一定要清楚每一行代码究竟是在整个程序中扮演者什么角色,执行哪一步操作。

实力有限,才疏学浅,如有错误,欢迎指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容