从零开始写一个爬虫实战

摘要:从零开始写一个爬虫实战。

作者 | Jack Cui

来源 | https://cuijiahua.com/blog/2017/10/spider_tutorial_1.html

建议:请在电脑的陪同下,阅读本文。本文以实战为主,阅读过程如稍有不适,还望多加练习。

网络爬虫简介

网络爬虫,也叫网络蜘蛛(Web Spider)。它根据网页地址(URL)爬取网页内容,而网页地址(URL)就是我们在浏览器中输入的网站链接。比如:https://www.baidu.com/,它就是一个 URL。

在讲解爬虫内容之前,我们需要先学习一项写爬虫的必备技能:审查元素(如果已掌握,可跳过此部分内容)

1、审查元素

在浏览器的地址栏输入 URL 地址,在网页处右键单击,找到检查。(不同浏览器的叫法不同,Chrome 浏览器叫做检查,Firefox 浏览器叫做查看元素,但是功能都是相同的)

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

我们可以看到,右侧出现了一大推代码,这些代码就叫做 HTML。什么是 HTML?举个容易理解的例子:我们的基因决定了我们的原始容貌,服务器返回的 HTML 决定了网站的原始容貌。

640?wx_fmt=png

Python3 网络爬虫快速入门实战解析

为啥说是原始容貌呢?因为人可以整容啊!扎心了,有木有?那网站也可以"整容"吗?可以!请看下图:

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

我能有这么多钱吗?显然不可能。我是怎么给网站"整容"的呢?就是通过修改服务器返回的 HTML 信息。我们每个人都是"整容大师",可以修改页面信息。我们在页面的哪个位置点击审查元素,浏览器就会为我们定位到相应的 HTML 位置,进而就可以在本地更改 HTML 信息。

再举个小例子:我们都知道,使用浏览器"记住密码"的功能,密码会变成一堆小黑点,是不可见的。可以让密码显示出来吗?可以,只需给页面"动个小手术"!以淘宝为例,在输入密码框处右键,点击检查。

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

可以看到,浏览器为我们自动定位到了相应的 HTML 位置。将下图中的 password 属性值改为 text 属性值(直接在右侧代码处修改):

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

我们让浏览器记住的密码就这样显现出来了:

640?wx_fmt=png

Python3 网络爬虫快速入门实战解析

说这么多,什么意思呢?浏览器就是作为客户端从服务器端获取信息,然后将信息解析,并展示给我们的。我们可以在本地修改 HTML 信息,为网页"整容",但是我们修改的信息不会回传到服务器,服务器存储的 HTML 信息不会改变。刷新一下界面,页面还会回到原本的样子。这就跟人整容一样,我们能改变一些表面的东西,但是不能改变我们的基因。

2、简单实例

网络爬虫的第一步就是根据 URL,获取网页的 HTML 信息。在 Python3 中,可以使用urllib.requestrequests进行网页爬取。

urllib 库是 python 内置的,无需我们额外安装,只要安装了 Python 就可以使用这个库。
requests 库是第三方库,需要我们自己安装。这个库强大好用,所以本文使用 requests 库获取网页的 HTML 信息。requests 库的 github 地址:https://github.com/requests/requests

(1)requests 安装

在 cmd 中,使用如下指令安装 requests:

1pip install requests

requests 库的基础方法如下:

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

官方中文教程地址:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html

requests 库的开发者为我们提供了详细的中文教程,查询起来很方便。本文不会对其所有内容进行讲解,摘取其部分使用到的内容,进行实战说明。

首先,让我们看下 requests.get()方法,它用于向服务器发起 GET 请求,不了解 GET 请求没有关系。我们可以这样理解:get 的中文意思是得到、抓住,那这个 requests.get()方法就是从服务器得到、抓住数据,也就是获取数据。让我们看一个例子(以 www.gitbook.cn 为例)来加深理解:

1# -*- coding:UTF-8 -*-
2import requests
3
4if __name__ == '__main__':
5    target = 'http://gitbook.cn/'
6    req = requests.get(url=target)
7    print(req.text)

requests.get()方法必须设置的一个参数就是 url,因为我们得告诉 GET 请求,我们的目标是谁,我们要获取谁的信息。运行程序看下结果:

640?wx_fmt=png

Python3 网络爬虫快速入门实战解析

左侧是我们程序获得的结果,右侧是我们在 www.gitbook.cn 网站审查元素获得的信息。我们可以看到,我们已经顺利获得了该网页的 HTML 信息。这就是一个最简单的爬虫实例,可能你会问,我只是爬取了这个网页的 HTML 信息,有什么用呢?客官稍安勿躁,接下来进入我们的实战正文。

爬虫实战

接下来我们来一次爬虫实战,爬取中网小说网站「笔趣看」上的文字。

(1)实战背景

小说网站-笔趣看:

URL:http://www.biqukan.com/

笔趣看是一个盗版小说网站,这里有很多起点中文网的小说,该网站小说的更新速度稍滞后于起点中文网正版小说的更新速度。并且该网站只支持在线浏览,不支持小说打包下载。因此,本次实战就是从该网站爬取并保存一本名为《一念永恒》的小说,该小说是耳根正在连载中的一部玄幻小说。PS:本实例仅为交流学习,支持耳根大大,请上起点中文网订阅。

(2)小试牛刀

我们先看下《一念永恒》小说的第一章内容,URL:http://www.biqukan.com/1_1094/5403177.html

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

我们先用已经学到的知识获取 HTML 信息试一试,编写代码如下:

1# -*- coding:UTF-8 -*-
2import requests
3
4if __name__ == '__main__':
5    target = 'http://www.biqukan.com/1_1094/5403177.html'
6    req = requests.get(url=target)
7    print(req.text)

运行代码,可以看到如下结果:

640?wx_fmt=jpeg
Python3 网络爬虫快速入门实战解析

可以看到,我们很轻松地获取了 HTML 信息。但是,很显然,很多信息是我们不想看到的,我们只想获得如右侧所示的正文内容,我们不关心 div、br 这些 html 标签。如何把正文内容从这些众多的 html 标签中提取出来呢?这就是本次实战的主要内容。

(3)Beautiful Soup

爬虫的第一步,获取整个网页的 HTML 信息,我们已经完成。接下来就是爬虫的第二步,解析 HTML 信息,提取我们感兴趣的内容。对于本小节的实战,我们感兴趣的内容就是文章的正文。提取的方法有很多,例如使用正则表达式、Xpath、Beautiful Soup 等。对于初学者而言,最容易理解,并且使用简单的方法就是使用 Beautiful Soup 提取感兴趣内容。

Beautiful Soup 的安装方法和 requests 一样,使用如下指令安装(也是二选一):

  1. pip install beautifulsoup4

  2. easy_install beautifulsoup4

一个强大的第三方库,都会有一个详细的官方文档。我们很幸运,Beautiful Soup 也是有中文的官方文档。URL:

http://beautifulsoup.readthedocs.io/zh_CN/latest/

同理,我会根据实战需求,讲解 Beautiful Soup 库的部分使用方法,更详细的内容,请查看官方文档。

现在,我们使用已经掌握的审查元素方法,查看一下我们的目标页面,你会看到如下内容:

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

不难发现,文章的所有内容都放在了一个名为 div 的“东西下面”,这个"东西"就是 html 标签。HTML 标签是 HTML 语言中最基本的单位,HTML 标签是 HTML 最重要的组成部分。不理解,没关系,我们再举个简单的例子:

一个女人的包包里,会有很多东西,她们会根据自己的习惯将自己的东西进行分类放好。镜子和口红这些会经常用到的东西,会归放到容易拿到的外侧口袋里。那些不经常用到,需要注意安全存放的证件会放到不容易拿到的里侧口袋里。

html 标签就像一个个“口袋”,每个“口袋”都有自己的特定功能,负责存放不同的内容。显然,上述例子中的 div 标签下存放了我们关心的正文内容。这个 div 标签是这样的:

1<div id="content"class="showtxt">

细心的朋友可能已经发现,除了 div 字样外,还有 id 和 class。id 和 class 就是 div 标签的属性,content 和 showtxt 是属性值,一个属性对应一个属性值。这东西有什么用?它是用来区分不同的 div 标签的,因为 div 标签可以有很多,我们怎么加以区分不同的 div 标签呢?就是通过不同的属性值。

仔细观察目标网站一番,我们会发现这样一个事实:class 属性为 showtxt 的 div 标签,独一份!这个标签里面存放的内容,是我们关心的正文部分。

知道这个信息,我们就可以使用 Beautiful Soup 提取我们想要的内容了,编写代码如下:

 1# -*- coding:UTF-8 -*-
2from bs4 import BeautifulSoup
3import requests
4if __name__ == "__main__":
5     target = 'http://www.biqukan.com/1_1094/5403177.html'
6     req = requests.get(url = target)
7     html = req.text
8     bf = BeautifulSoup(html)
9     texts = bf.find_all('div', class_ = 'showtxt'
10     print(texts)

在解析 html 之前,我们需要创建一个 Beautiful Soup 对象。BeautifulSoup 函数里的参数就是我们已经获得的 html 信息。然后我们使用find_all方法,获得 html 信息中所有 class 属性为 showtxt 的 div 标签。find_all方法的第一个参数是获取的标签名,第二个参数class_是标签的属性,为什么不是 class,而带了一个下划线呢?因为 python 中 class 是关键字,为了防止冲突,这里使用class_表示标签的 class 属性,class_后面跟着的 showtxt 就是属性值了。看下我们要匹配的标签格式:

1<div id="content"class="showtxt">

这样对应的看一下,是不是就懂了?可能有人会问了,为什么不是find_all('div', id = 'content', class_ = 'showtxt')?这样其实也是可以的,属性是作为查询时候的约束条件,添加一个class_='showtxt'条件,我们就已经能够准确匹配到我们想要的标签了,所以我们就不必再添加 id 这个属性了。运行代码查看我们匹配的结果:

640?wx_fmt=png

Python3 网络爬虫快速入门实战解析

我们可以看到,我们已经顺利匹配到我们关心的正文内容,但是还有一些我们不想要的东西。比如 div 标签名,br 标签,以及各种空格。怎么去除这些东西呢?我们继续编写代码:

 1# -*- coding:UTF-8 -*-
2from bs4 import BeautifulSoup
3import requests
4if __name__ == "__main__":
5     target = 'http://www.biqukan.com/1_1094/5403177.html'
6     req = requests.get(url = target) 
7     html = req.text
8     bf = BeautifulSoup(html)
9     texts = bf.find_all('div', class_ = 'showtxt')
10     print(texts[0].text.replace('\xa0'*8,'\n\n'))

find_all 匹配的返回的结果是一个列表。提取匹配结果后,使用 text 属性,提取文本内容,滤除 br 标签。随后使用 replace 方法,剔除空格,替换为回车进行分段。&nbsp;在 html 中是用来表示空格的。replace('\xa0'*8,'\n\n')就是去掉下图的八个空格符号,并用回车代替:

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

程序运行结果如下:

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

可以看到,我们很自然的匹配到了所有正文内容,并进行了分段。我们已经顺利获得了一个章节的内容,要想下载正本小说,我们就要获取每个章节的链接。我们先分析下小说目录:URL:http://www.biqukan.com/1_1094/

640?wx_fmt=png

Python3 网络爬虫快速入门实战解析

通过审查元素,我们发现可以发现,这些章节都存放在了 class 属性为 listmain 的 div 标签下,选取部分 html 代码如下:

 1<div class="listmain">
2<dl>
3<dt>《一念永恒》最新章节列表</dt>
4<dd><a href="/1_1094/15932394.html">第 1027 章 第十道门</a></dd>
5<dd><a href="/1_1094/15923072.html">第 1026 章 绝伦道法!</a></dd>
6<dd><a href="/1_1094/15921862.html">第 1025 章 长生灯!</a></dd>
7<dd><a href="/1_1094/15918591.html">第 1024 章 一目晶渊</a></dd>
8<dd><a href="/1_1094/15906236.html">第 1023 章 通天道门</a></dd>
9<dd><a href="/1_1094/15903775.html">第 1022 章 四大凶兽!</a></dd>
10<dd><a href="/1_1094/15890427.html">第 1021 章 鳄首!</a></dd>
11<dd><a href="/1_1094/15886627.html">第 1020 章 一触即发!</a></dd>
12<dd><a href="/1_1094/15875306.html">第 1019 章 魁祖的气息!</a></dd>
13<dd><a href="/1_1094/15871572.html">第 1018 章 绝望的魁皇城</a></dd>
14<dd><a href="/1_1094/15859514.html">第 1017 章 我还是恨你!</a></dd>
15<dd><a href="/1_1094/15856137.html">第 1016 章 从来没有世界之门!</a></dd>
16<dt>《一念永恒》正文卷</dt> <dd><a href="/1_1094/5386269.html">外传 1 柯父。</a></dd>
17<dd><a href="/1_1094/5386270.html">外传 2 楚玉嫣。</a></dd> <dd><a href="/1_1094/5386271.html">外传 3 鹦鹉与皮冻。</a></dd>
18<dd><a href="/1_1094/5403177.html">第一章 他叫白小纯</a></dd> <dd><a href="/1_1094/5428081.html">第二章 火灶房</a></dd>
19<dd><a href="/1_1094/5433843.html">第三章 六句真言</a></dd> <dd><a href="/1_1094/5447905.html">第四章 炼灵</a></dd>
20</dl>
21</div>

在分析之前,让我们先介绍一个概念:父节点、子节点、孙节点。<div></div>限定了<div>标签的开始和结束的位置,他们是成对出现的,有开始位置,就有结束位置。我们可以看到,在<div>标签包含<dl>标签,那这个<dl>标签就是<div>标签的子节点,<dl>标签又包含<dt>标签和<dd>标签,那么<dt>标签和<dd>标签就是<div>标签的孙节点。有点绕?那你记住这句话:谁包含谁,谁就是谁儿子!

他们之间的关系都是相对的。比如对于<dd>标签,它的子节点是<a>标签,它的父节点是<dl>标签。这跟我们人是一样的,上有老下有小。

看到这里可能有人会问,这有好多<dd>标签和<a>标签啊!不同的<dd>标签,它们是什么关系啊?显然,兄弟姐妹喽!我们称它们为兄弟结点。

好了,概念明确清楚,接下来,让我们分析一下问题。我们看到每个章节的名字存放在了<a>标签里面。<a>标签还有一个 href 属性。这里就不得不提一下<a> 标签的定义了,<a> 标签定义了一个超链接,用于从一张页面链接到另一张页面。<a> 标签最重要的属性是 href 属性,它指示链接的目标。

我们将之前获得的第一章节的 URL 和<a> 标签对比看一下:

1http://www.biqukan.com/1_1094/5403177.html

不难发现,<a> 标签中 href 属性存放的属性值/1_1094/5403177.html是章节 URLhttp://www.biqukan.com/1_1094/5403177.html的后半部分。其他章节也是如此!那这样,我们就可以根据<a> 标签的 href 属性值获得每个章节的链接和名称了。

总结一下:小说每章的链接放在了 class 属性为 listmain 的<div>标签下的<a>标签中。链接具体位置放在 html->body->div->dl->dd->a 的 href 属性中。先匹配 class 属性为 listmain 的<div>标签,再匹配<a>标签。编写代码如下:

 1# -*- coding:UTF-8 -*-
2from bs4 import BeautifulSoup
3import requests
4if __name__ == "__main__":
5     target = 'http://www.biqukan.com/1_1094/'
6     req = requests.get(url = target)
7     html = req.text
8     div_bf = BeautifulSoup(html)
9     div = div_bf.find_all('div', class_ = 'listmain')
10     print(div[0])

还是使用 find_all 方法,运行结果如下:

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

很顺利,接下来再匹配每一个<a>标签,并提取章节名和章节文章。如果我们使用 Beautiful Soup 匹配到了下面这个<a>标签,如何提取它的 href 属性和<a>标签里存放的章节名呢?

1<a href="/1_1094/5403177.html">第一章 他叫白小纯</a>

方法很简单,对 Beautiful Soup 返回的匹配结果 a,使用 a.get('href')方法就能获取 href 的属性值,使用 a.string 就能获取章节名,编写代码如下:

 1# -*- coding:UTF-8 -*-
2from bs4 import BeautifulSoup
3import requests
4if __name__ == "__main__":
5     server = 'http://www.biqukan.com/'
6     target = 'http://www.biqukan.com/1_1094/'
7     req = requests.get(url = target) html = req.text
8     div_bf = BeautifulSoup(html)
9     div = div_bf.find_all('div', class_ = 'listmain')
10     a_bf = BeautifulSoup(str(div[0]))
11     a = a_bf.find_all('a')
12     for each in a:
13          print(each.string, server + each.get('href'))

因为 find_all 返回的是一个列表,里边存放了很多的<a>标签,所以使用 for 循环遍历每个<a>标签并打印出来,运行结果如下:

640?wx_fmt=png
Python3 网络爬虫快速入门实战解析

最上面匹配的一千多章的内容是最新更新的 12 章节的链接。这 12 章内容会和下面的重复,所以我们要滤除,除此之外,还有那 3 个外传,我们也不想要。这些都简单地剔除就好。

(3)整合代码

每个章节的链接、章节名、章节内容都有了。接下来就是整合代码,将获得内容写入文本文件存储就好了。编写代码如下:

 1# -*- coding:UTF-8 -*-
2from bs4 import BeautifulSoup
3import requests, sys
4
5class downloader(object):
6    def __init__(self):
7        self.server = 'http://www.biqukan.com/'
8        self.target = 'http://www.biqukan.com/1_1094/'
9        self.names = []            #存放章节名
10        self.urls = []            #存放章节链接
11        self.nums = 0            #章节数
12
13    def get_download_url(self):
14        req = requests.get(url = self.target)
15        html = req.text
16        div_bf = BeautifulSoup(html)
17        div = div_bf.find_all('div', class_ = 'listmain')
18        a_bf = BeautifulSoup(str(div[0]))
19        a = a_bf.find_all('a')
20        self.nums = len(a[15:])                                #剔除不必要的章节,并统计章节数
21        for each in a[15:]:
22            self.names.append(each.string)
23            self.urls.append(self.server + each.get('href'))
24
25    """
26    函数说明:获取章节内容
27    Parameters:
28        target - 下载连接(string)
29    Returns:
30        texts - 章节内容(string)
31    """

32    def get_contents(self, target):
33        req = requests.get(url = target)
34        html = req.text
35        bf = BeautifulSoup(html)
36        texts = bf.find_all('div', class_ = 'showtxt')
37        texts = texts[0].text.replace('\xa0'*8,'\n\n')
38        return texts
39
40    """
41    函数说明:将爬取的文章内容写入文件
42    Parameters:
43        name - 章节名称(string)
44        path - 当前路径下,小说保存名称(string)
45        text - 章节内容(string)
46    Returns:
47        无
48    """

49    def writer(self, name, path, text):
50        write_flag = True
51        with open(path, 'a', encoding='utf-8'as f:
52            f.write(name + '\n')
53            f.writelines(text)
54            f.write('\n\n')
55
56if __name__ == "__main__":
57    dl = downloader()
58    dl.get_download_url()
59    print('《一年永恒》开始下载:')
60    for i in range(dl.nums):
61        dl.writer(dl.names[i], '一念永恒.txt', dl.get_contents(dl.urls[i]))
62        sys.stdout.write("  已下载:%.3f%%" %  float(i/dl.nums*100) + '\r')
63        sys.stdout.flush()
64    print('《一年永恒》下载完成')

很简单的程序,单进程跑,没有开进程池。下载速度略慢,喝杯茶休息休息吧。代码运行效果如下图所示:

640?wx_fmt=gif
Python3 网络爬虫快速入门实战解析

以上就是一次爬虫实战。

本文转自 Jack Cui 的博客文章,公众号见下方:

640?wx_fmt=png




/今日留言主题/

你觉得这个爬虫难不难,或者爬过什么有意思网站?

(留言格式:Dayxx:blahblah

字数限制:15 字以上.)

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习Python网络爬虫可以分为以下几个步骤: 1. 学习Python基础知识:掌握Python的基本语法、变量、数据类型、函数、模块、文件操作等知识。 2. 学习HTTP协议和HTML语言:了解HTTP协议和HTML语言的基本知识,包括HTTP请求和响应、HTML标签和结构等。 3. 学习爬虫框架和库:学习使用Python中的爬虫框架和库,如requests、Beautiful Soup、Scrapy等。 4. 爬取网页数据:通过爬虫框架和库,编Python脚本实现爬取网页数据的功能,如获取网页源代码、解析HTML结构、提取数据等。 5. 存储和处理数据:将爬取到的数据存储到本地文件或数据库中,并进行数据处理和分析。 以下是具体的学习步骤: Step 1:学习Python基础知识 推荐学习资料:《Python编程快速上手》、《Python基础教程》等。 Step 2:学习HTTP协议和HTML语言 推荐学习资料:《图解HTTP》、《HTML和CSS设计和构建网站》等。 Step 3:学习爬虫框架和库 推荐学习资料:官方文档、《Python爬虫开发与项目实战》等。 Step 4:爬取网页数据 以爬取豆瓣电影Top250为例,以下是一个简单的Python爬虫: ```python import requests from bs4 import BeautifulSoup url = 'https://movie.douban.com/top250' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'} response = requests.get(url, headers=headers) soup = BeautifulSoup(response.text, 'html.parser') movies = soup.find_all('div', class_='hd') for movie in movies: print(movie.a.span.text) ``` 该爬虫使用requests库发送HTTP请求,使用Beautiful Soup库解析HTML结构,获取豆瓣电影Top250的电影名。 Step 5:存储和处理数据 可使用Python内置的文件操作函数或第三方库如pandas、numpy等对爬取到的数据进行存储和处理。 以上是从零开始学习Python网络爬虫的基本步骤,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值