[TOC]

from:https://blog.csdn.net/c406495762/article/details/78123502

前言

本文的实战内容有:

  • 网络小说下载(静态网站)
  • 优美壁纸下载(动态网站)
  • 视频下载

    网络爬虫简介

    网络爬虫,也叫网络蜘蛛(Web Spider)。它根据网页地址(URL)爬取网页内容,而网页地址(URL)就是我们在浏览器中输入的网站链接。比如https://www.baidu.com/,它就是一个URL。
    在讲解爬虫内容之前,我们需要先学习一项写爬虫的必备技能:审查元素。

    1 审查元素

    在浏览器的地址栏输入URL地址,在网页处右键单击,找到检查。(不同浏览器的叫法不同,Chrome浏览器叫做检查,Firefox浏览器叫做查看元素,但是功能都是相同的)
    我们可以看到,右侧出现了一大推代码,这些代码就叫做HTML。什么是HTML?举个容易理解的例子:我们的基因决定了我们的原始容貌,服务器返回的HTML决定了网站的原始容貌。
    为啥说是原始容貌呢?因为人可以整容啊!那网站也可以“整容”吗?可以!怎么给网站“整容”的呢?就是通过修改服务器返回的HTML信息。我们在页面的哪个位置点击审查元素,浏览器就会为我们定位到相应的HTML位置,进而就可以在本地更改HTML信息。

再举个例子:我们都知道,使用浏览器”记住密码”的功能,密码会变成一堆小黑点,是不可见的。可以让密码显示出来吗?可以!

  • 以淘宝为例,在输入密码框处右键,点击检查;
  • 可以看到,浏览器为我们自动定位到了相应的HTML位置。将下图中的password属性值改为text属性值(直接在右侧代码处修改);
  • 我们让浏览器记住的密码就这样显现出来了。

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

2 简单实例

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

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

requests库的基础方法如下:
官方中文教程地址:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html
首先,让我们看下requests.get()方法,它用于向服务器发起GET请求,不了解GET请求没有关系。我们可以这样理解:get的中文意思是得到、抓住,那这个requests.get()方法就是从服务器得到、抓住数据,也就是获取数据。让我们看一个例子(以 www.gitbook.cn为例)来加深理解:

# -- coding:UTF-8 --
import requests
if name == ‘main‘:
target = ‘http://gitbook.cn/
req = requests.get(url=target)
print(req.text)

requests.get()方法必须设置的一个参数就是url,因为我们得告诉GET请求,我们的目标是谁,我们要获取谁的信息。这样就爬取了这个网页的HTML信息。

三 爬虫实战

1 小说下载

(1) 实战背景
小说网站-笔趣看:URL:http://www.biqukan.com/
本次实战就是从该网站爬取并保存一本名为《一念永恒》的小说。
(2) 小试牛刀
我们先看下《一念永恒》小说的第一章内容,URL:http://www.biqukan.com/1_1094/5403177.html
我们先用已经学到的知识获取HTML信息试一试,编写代码如下:

# -- coding:UTF-8 --
import requests

if name == ‘main‘:
target = ‘http://www.biqukan.com/1_1094/5403177.html
req = requests.get(url=target)
print(req.text)

运行代码,可以看到如下结果
可以看到,我们很轻松地获取了HTML信息。但是,很显然,很多信息是我们不想看到的,我们只想获得如右侧所示的正文内容,我们不关心div、br这些html标签。如何把正文内容从这些众多的html标签中提取出来呢?这就是本次实战的主要内容。
(3)Beautiful Soup
爬虫的第一步,获取整个网页的HTML信息,我们已经完成。接下来就是爬虫的第二步,解析HTML信息,提取我们感兴趣的内容。
对于本小节的实战,我们感兴趣的内容就是文章的正文。提取的方法有很多,例如使用正则表达式、Xpath、Beautiful Soup等。对于初学者而言,最容易理解,并且使用简单的方法就是使用Beautiful Soup提取感兴趣内容。
Beautiful Soup也是有中文的官方文档:http://beautifulsoup.readthedocs.io/zh_CN/latest/

现在,我们使用已经掌握的审查元素方法,查看一下我们的目标页面,不难发现,文章的所有内容都放在了一个名为div的“东西下面”,这个”东西”就是html标签。HTML标签是HTML语言中最基本的单位,HTML标签是HTML最重要的组成部分。html标签就像一个个“口袋”,每个“口袋”都有自己的特定功能,负责存放不同的内容。显然,上述例子中的div标签下存放了我们关心的正文内容。这个div标签是这样的:


细心的朋友可能已经发现,除了div字样外,还有id和class。id和class就是div标签的属性,content和showtxt是属性值,一个属性对应一个属性值。这东西有什么用?它是用来区分不同的div标签的,因为div标签可以有很多,我们怎么加以区分不同的div标签呢?就是通过不同的属性值。
仔细观察目标网站一番,我们会发现这样一个事实:class属性为showtxt的div标签,独一份!这个标签里面存放的内容,是我们关心的正文部分。知道这个信息,我们就可以使用Beautiful Soup提取我们想要的内容了,编写代码如下:

# -- coding:UTF-8 --
from bs4 import BeautifulSoup
import requests
if name == “main“:
target = ‘http://www.biqukan.com/1_1094/5403177.html
req = requests.get(url = target)
html = req.text
bf = BeautifulSoup(html)
texts = bf.find_all(‘div’, class = ‘showtxt’) print(texts)_

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


这样对应的看一下,是不是就懂了?可能有人会问了,为什么不是findall(‘div’, id = ‘content’, class = ‘showtxt’)?这样其实也是可以的,属性是作为查询时候的约束条件,添加一个class_=’showtxt’条件,我们就已经能够准确匹配到我们想要的标签了,所以我们就不必再添加id这个属性了。运行代码查看我们匹配的结果。
我们可以看到,我们已经顺利匹配到我们关心的正文内容,但是还有一些我们不想要的东西。比如div标签名,br标签,以及各种空格。怎么去除这些东西呢?

# -- coding:UTF-8 --
from bs4 import BeautifulSoup
import requests
if name == “main“:
target = ‘http://www.biqukan.com/1_1094/5403177.html
req = requests.get(url = target) html = req.text
bf = BeautifulSoup(html)
texts = bf.find_all(‘div’, class = ‘showtxt’)
print(texts[0].text.replace(‘\xa0’*8,’\n\n’))_

find_all匹配的返回的结果是一个列表。提取匹配结果后,使用text属性,提取文本内容,滤除br标签。随后使用replace方法,剔除空格,替换为回车进行分段。 在html中是用来表示空格的。replace(‘\xa0’*8,’\n\n’)就是去掉下图的八个空格符号,并用回车代替。
可以看到,我们很自然的匹配到了所有正文内容,并进行了分段。我们已经顺利获得了一个章节的内容,要想下载正本小说,我们就要获取每个章节的链接。

我们先分析下小说目录:http://www.biqukan.com/1_1094/
通过审查元素,我们发现可以发现,这些章节都存放在了class属性为listmain的div标签下,选取部分html代码如下:

在分析之前,让我们先介绍一个概念:父节点、子节点、孙节点。

限定了
标签的开始和结束的位置,他们是成对出现的,有开始位置,就有结束位置。我们可以看到,在
标签包含
标签,那这个
标签就是
标签的子节点,
标签又包含
标签和
标签,那么
标签和
标签就是
标签的孙节点。
他们之间的关系都是相对的。比如对于
标签,它的子节点是标签,它的父节点是
标签。
看到这里可能有人会问,这有好多
标签和标签啊!不同的
标签,它们是什么关系啊?我们称它们为兄弟结点。
我们分析一下问题。我们看到每个章节的名字存放在了
标签里面。标签还有一个href属性。这里就不得不提一下标签的定义了,标签定义了一个超链接,用于从一张页面链接到另一张页面。 标签最重要的属性是 href 属性,它指示链接的目标。
我们将之前获得的第一章节的URL和
标签对比看一下:http://www.biqukan.com/1_1094/5403177.html
第一章 他叫白小纯
不难发现, 标签中href属性存放的属性值/1_1094/5403177.html是章节URLhttp://www.biqukan.com/1_1094/5403177.html的后半部分。其他章节也是如此!那这样,我们就可以根据标签的href属性值获得每个章节的链接和名称了。
总结一下:小说每章的链接放在了class属性为listmain的
标签下的标签中。链接具体位置放在html->body->div->dl->dd->a的href属性中。先匹配class属性为listmain的
标签,再匹配标签。编写代码如下:

# -- coding:UTF-8 --
from bs4 import BeautifulSoup
import requests
if name == “main“:
target = ‘
http://www.biqukan.com/1_1094/
req = requests.get(url = target)
html = req.text
div_bf = BeautifulSoup(html)
div = div_bf.find_all(‘div’, class = ‘listmain’)
print(div[0])_

还是使用find_all方法,接下来再匹配每一个标签,并提取章节名和章节文章。如果我们使用Beautiful Soup匹配到了下面这个标签,如何提取它的href属性和标签里存放的章节名呢?
第一章 他叫白小纯
方法很简单,对Beautiful Soup返回的匹配结果a,使用a.get(‘href’)方法就能获取href的属性值,使用a.string就能获取章节名,编写代码如下:

# -- coding:UTF-8 --
from bs4 import BeautifulSoup
import requests
if name == “main“:
server = ‘http://www.biqukan.com/
target = ‘http://www.biqukan.com/1_1094/
req = requests.get(url = target) html = req.text
div_bf = BeautifulSoup(html)
div = div_bf.find_all(‘div’, class = ‘listmain’)
abf = BeautifulSoup(str(div[0]))
a = a_bf.find_all(‘a’)
for each in a:
print(each.string, server + each.get(‘href’))

因为find_all返回的是一个列表,里边存放了很多的标签,所以使用for循环遍历每个标签并打印出来。
最上面匹配的一千多章的内容是最新更新的12章节的链接。这12章内容会和下面的重复,所以我们要滤除,除此之外,还有那3个外传,我们也不想要。这些都简单地剔除就好。
(3)整合代码
每个章节的链接、章节名、章节内容都有了。接下来就是整合代码,将获得内容写入文本文件存储就好了。编写代码如下:

# -- coding:UTF-8 --
from bs4 import BeautifulSoup
import requests, sys

“””
类说明:下载《笔趣看》网小说《一念永恒》
Parameters:

Returns:

Modify:
2017-09-13
“””
class downloader(object):

def init(self):
self.server = ‘
http://www.biqukan.com/
self.target = ‘http://www.biqukan.com/1_1094/
self.names = [] #存放章节名
self.urls = [] #存放章节链接
self.nums = 0 #章节数

“””
函数说明:获取下载链接
Parameters:

Returns:

Modify:
2017-09-13
“””
def get_download_url(self):
req = requests.get(url = self.target)
html = req.text
div_bf = BeautifulSoup(html)
div = div_bf.find_all(‘div’, class = ‘listmain’)
abf = BeautifulSoup(str(div[0]))
a = a_bf.find_all(‘a’)
self.nums = len(a[15:]) #剔除不必要的章节,并统计章节数
for each in a[15:]:
self.names.append(each.string)
self.urls.append(self.server + each.get(‘href’))

“””
函数说明:获取章节内容
Parameters:
target - 下载连接(string)
Returns:
texts - 章节内容(string)
Modify:
2017-09-13
“””
def get_contents(self, target):
req = requests.get(url = target)
html = req.text
bf = BeautifulSoup(html)
texts = bf.find_all(‘div’, class = ‘showtxt’)
texts = texts[0].text.replace(‘\xa0’*8,’\n\n’)
return texts_

“””
函数说明:将爬取的文章内容写入文件
Parameters:
name - 章节名称(string)
path - 当前路径下,小说保存名称(string)
text - 章节内容(string)
Returns:

Modify:
2017-09-13
“””
def writer(self, name, path, text):
write_flag = True
with open(path, ‘a’, encoding=’utf-8’) as f:
f.write(name + ‘\n’)
f.writelines(text)
f.write(‘\n\n’)

if name == “main“:
dl = downloader()
dl.get_download_url()
print(‘《一年永恒》开始下载:’)
for i in range(dl.nums):
dl.writer(dl.names[i], ‘一念永恒.txt’, dl.get_contents(dl.urls[i]))
sys.stdout.write(“ 已下载:%.3f%%” % float(i/dl.nums) + ‘\r’)
sys.stdout.flush()
print(‘《一年永恒》下载完成’)

很简单的程序,单进程跑,没有开进程池。下载速度略慢。

2 优美壁纸下载

(1)实战背景
已经会爬取文字了,是不是感觉爬虫还是蛮好玩的呢?接下来,让我们进行一个进阶实战,了解一下反爬虫。
URL:https://unsplash.com/
看一看这些优美的壁纸,这个网站的名字叫做Unsplash,免费高清壁纸分享网是一个坚持每天分享高清的摄影图片的站点,每天更新一张高质量的图片素材,全是生活中的景象作品,清新的生活气息图片可以作为桌面壁纸也可以应用于各种需要的环境。
(2)实战进阶
我们已经知道了每个html标签都有各自的功能。标签存放一下超链接,图片存放在哪个标签里呢?html规定,图片统统给我放到爬虫-0719 - 图1标签中!既然这样,我们截取就Unsplash网站中的一个爬虫-0719 - 图2标签,分析一下:
Snow-capped mountain slopes under blue sky
可以看到,爬虫-0719 - 图4标签有很多属性,有alt、src、class、style属性,其中src属性存放的就是我们需要的图片保存地址,我们根据这个地址就可以进行图片的下载。
那么,让我们先捋一捋这个过程:

  1. 使用requeusts获取整个网页的HTML信息;
  2. 使用Beautiful Soup解析HTML信息,找到所有爬虫-0719 - 图5标签,提取src属性,获取图片存放地址;
  3. 根据图片存放地址,下载图片。

我们信心满满地按照这个思路爬取Unsplash试一试,编写代码如下:

# -- coding:UTF-8 --
import requests
if name == ‘main‘:
target = ‘
https://unsplash.com/
req = requests.get(url=target)
print(req.text)

按照我们的设想,我们应该能找到很多爬虫-0719 - 图6标签。但是我们发现,除了一些