爬取豆瓣书籍列表

合理爬取,不随意扩大站点压力
本文章仅作示例,请勿用作非法用途


效果

  解析豆瓣书籍列表,包括作者/介绍/评分,如下图

效果

开始

  和上文一样,我们先去网页端看看我们准备获取的数据
在这里插入图片描述
  随便点进去一个子分类
在这里插入图片描述
  右键——查看框架源代码 (Edge,其他浏览器请自行对应 )
在这里插入图片描述
  哇塞,又是一下子就找到了。
在这里插入图片描述
  接下来,开始爬取!

试爬取

  根据前几节课的,我们写一版简单的代码试试
在这里插入图片描述
  执行一下!
在这里插入图片描述
  woc!报错!我们来看看这个418是个什么错误

错误描述:经过网上查询得知,418的意思是被网站的反爬程序返回的,网上解释为,418 I’m a teapot
The HTTP 418 I’m a teapot client error response code indicates that the server refuses to brew coffee because it is a teapot. This error is a reference to Hyper Text Coffee Pot Control Protocol which was an April Fools’ joke in 1998.
翻译为:HTTP 418 I’m a teapot客户端错误响应代码表示服务器拒绝煮咖啡,因为它是一个茶壶。这个错误是对1998年愚人节玩笑的超文本咖啡壶控制协议的引用。

好吧,我们又被bang了,但是没关系,我们有乔装术!

UA伪装

  同上节课一样,我们看一下自己的UA
在这里插入图片描述
(顺带一提,在这个过程中,我发现了一个豆瓣很有趣的玩意:
在这里插入图片描述
挺神奇的)
  依葫芦画瓢,简单伪装一下:
在这里插入图片描述
真的是非常的amazing啊,搞定了!

解析数据

  有了网页,我们开始解析数据。
  前几节课我们一直使用的是纯正则表达式,但是既然是学习,我们来整点不一样的

BeautifulSoup4

  看到这个题目,你会不会以为我打错了?美丽的汤?这是什么玩意?
  事实上我没有打错,bs4是Python爬虫中相当著名的一个包,在解析html时非常易于使用。当然,它也有自己的弊端,例如相较于re,用它解析大量数据速度会慢很多。当然这里并不影响。
  如果想学习bs4的话,可以先访问这里,看一下它的基本使用。接下来我们就直接开始。

安装

使用pip安装

1
pip install beautifulsoup4

在这里插入图片描述

使用

1
from bs4 import BeautifulSoup

在这里插入图片描述
  将获取到的html文本作为参数传入,实例化一个BeautifulSoup对象,使用默认的html解析器
  然后我们使用find_all方法找到href属性中包含subject的所有标签并且打印试一下
在这里插入图片描述
amazing啊,一下子就找到了所有!
如法炮制,我们再找一下其他的

在这里插入图片描述
  然而到这里我发现了一个小问题,我们分别提取了 书名、作者、简介,这是这样合在一起似乎不太方便。所以我决定换一种思路,按照豆瓣一本书一本书的爬取,然后再分别提取。
  我们来看看它们的一本书对应的代码是怎么写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="detail-frame">
<h2>
<a href="https://book.douban.com/subject/35023731/">怪诞故事集</a>
</h2>
<p class="rating">
<span class="allstar00"></span>
<span class="font-small color-lightgray">
</span>
</p>
<p class="color-gray">
[波兰] 奥尔加·托卡尔丘克 / 浙江文艺出版社 / 2020-7
</p>
<p class="detail">
诺奖得主奥尔加·托卡尔丘克小说集,讲述了十个怪诞、疯狂、恐怖和幽默的故事,融合了民间传说、童话、科幻、宗教故事等
元素来观照波兰历史与人的生活。
</p>
</div>

  可以看到,每本书都在class=”detail-frame”的div里面。按照这个要求,我们重新爬取全部书籍列表。
在这里插入图片描述
接下来分别解析

使用lxml

  对于这样的单独提取出来的<div></div>来说,直接用html解析方式怕是不ok了,那我们怎么办?难道又回到写正则的苦逼日子?
  不要慌,bs4的解析器除了”html.parser”,还可以直接使用lxml。
  什么是lxml?

lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高。这个库的主要优点是易于使用,在解析大型文档时速度非常快,归档的也非常好,并且提供了简单的转换方法来将数据转换为Python数据类型,从而使文件操作更容易。
XPath,全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言,它最初是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索
XPath的选择功能十分强大,它提供了非常简明的路径选择表达式,另外,它还提供了超过100个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等,几乎所有我们想要定位的节点,都可以用XPath来选择
XPath于1999年11月16日成为W3C标准,它被设计为供XSLT、XPointer以及其他XML解析软件使用,更多的文档可以访问其官方网站:https://www.w3.org/TR/xpath/

体验一下?

安装

依旧使用pip安装

1
pip install lxml

在这里插入图片描述

结合bs4使用

  • 书籍名在 h2标题下
  • 作者信息在 class为color-gray的 p标签下
  • 简介在 class为detail的 p标签下

在这里插入图片描述
  但是很尴尬的是,这样做在执行到后半部分的时候会报错
在这里插入图片描述
  原因是有一部分书籍的 简介 直接放在了 p 标签下,而并没有class属性。这也算是一种特殊情况吧(嘤嘤嘤)
  没有办法,处理一下特殊情况。完美
在这里插入图片描述

  最后闲的蛋疼,我们再把评分补上。大功告成!

完整源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#FunnySaltyFish 2020/07/09 完成
#基于Python 3.7.0
import urllib.request as ur
from bs4 import BeautifulSoup
import re


def get_html(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36 Edg/83.0.478.61'
}
request = ur.Request(url=url, headers=headers)
html = ur.urlopen(request).read()
return html.decode('utf-8')


def parse_html(html):
soup = BeautifulSoup(html, 'html.parser')
# books_list = soup.find_all(href=re.compile(r"https://book.douban.com/subject/"))
# authors_list = soup.find_all(class_=re.compile("color-gray"))
# details_list = soup.find_all(class_=re.compile("detail"))
# print(books_list)
# print(authors_list)
# print(details_list)
detail_frames = soup.find_all(class_=re.compile("detail-frame"))
result_list = []
for each_detail in detail_frames:
result_list.append(parse_one_book(str(each_detail)))
return '\n'.join(result_list)


def parse_one_book(detail):
soup = BeautifulSoup(detail, 'lxml')
name = soup.div.h2.a.string
author = soup.find(
'p', attrs={'class': 'color-gray'}).string.strip() # 去除无效字符
rating = soup.find(
'span', attrs={'class': 'font-small color-lightgray'}).string.strip()
if len(str(rating)) == 0:
rating = '暂无评分'

if soup.find('p', attrs={'class': 'detail'}):
detail = soup.find(
'p', attrs={'class': 'detail'}).string.strip() # 去除无效字符
else:
detail = soup.find(
'p', attrs={'class': 'color-gray'}).find_next().string.strip()
result = f"-->%s (%s) 评分:%s:\n %s" % (name, author, rating, detail)
return result


if __name__ == "__main__":
html = get_html('https://book.douban.com/latest')
result = parse_html(html)
print(result)

其他