——————————本文仅用于技术交流,支持正版——————————
爬虫学到了一丢丢,就开始了实战之旅,第一次实战,来点简单的,我们来爬一本小说。对网页结构进行分析
网上随便找了本小说,按下我们最热爱的F12
,打开开发者工具,按下图所示操作。
点击开发者工具左上角的小箭头,鼠标指向章节链接的位置,不要点击!开发者工具就会自动显示这一部分所对应的源代码,我们能发现每个章节的链接都是在a标签。我们就可以用正则表达式将每个章节的链接都找出来。
而每一章节的内容是这样的:我们再查看网页的源代码,如下图:
含无用的script发现不仅有我们所需要的小说内容,还有一些无用的script。之后还需要处理。
获取网页的请求头
Headers我们以这个章节目录为例,打开开发者工具,点击Network
,会出现如图所示界面,若没有,刷新一下即可。然后点击3392/,而我们所需要的在Request Headers
里。将该目录下的信息提取,存放到字典中,其中最重要的是User-Agent
,仅将其存放到我们的headers字典中也行,其代表了我们的身份信息,浏览器的User-Agent
一般都有Mozilla/5.0
。
headers = {
"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
}
所需要的库文件
-
requests:用于get请求
-
bs4:网页解析
-
re:正则表达式
-
os:系统相关操作
-
time:获取时间
-
random:得到一个随机数
import requests, re, os, time, random
from bs4 import BeautifulSoup
获得章节目录的链接
headers = {
"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
}
try:
r = requests.get(url, headers=headers)
#get请求的状态,get失败会报错
r.raise_for_status()
#修改get到的编码
r.encoding = r.apparent_encoding
except:
print("爬取失败")
#用正则表达式获取每一章节的url
urls = re.findall('<li ><a href="(.*?)">.*?</a></li>', r.text)</pre>
关于requests
的操作可以参考我之前写的博客:
小白学爬虫——Requests.get()
小白学爬虫——爬取网页的基本框架
这里讲一下re.findall
findall
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。 .*?
表示中间为任意字符串,而加了(.*?)
表示仅保留括号中的内容。
这里我们就把含链接的源代码复制过来,链接部分用(.*?)
代替,而其余无用项且不相同的部分用.*?
代替,即可保存括号中的内容,即a标签中的链接。 得到urls。
获取单章的内容
由于得到的urls只有后半部分,所以我们需要手动添加前半部分的url。
url_source = "https://www.boquge.com"
url = url_source + urls[0]
try:
r = requests.get(url, headers = headers)
r.raise_for_status()
r.encoding = r.apparent_encoding
except:
print("爬取失败")
接下来对返回的response对象进行处理。
title = re.findall('<div id="h1" class="text-center"><h1>(.*?)</h1><br/>', r.text)
soup = BeautifulSoup(r.text, "html.parser")
texts = soup.select('#chapter #txtContent')[0]
for ss in texts.select("script"):
#删除无用项
ss.decompose()
#按照指定格式替换章节内容,运用正则表达式
text=re.sub( '\s+', '\r\n\t', texts.text).strip('\r\n')</pre>
得到标题的方法已经讲过了,现在要讲BeautifulSoup了。
BeautifulSoup
,又称美丽汤,提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。 而"html.parser"
是用来指定BeautifulSoup
的解析器的。除此之外还有不少解析器,不再一一举例。
而.select()
方法是对标签进行查找和筛选,返回一个列表,筛选方法是:
-
标签名不加任何修饰
-
类名前加点
-
id名前加 #
-
上下级之间用空格隔开
第一级id=“chapter”,第二级id=“txtContent”
texts = soup.select('#chapter #txtContent')[0]
上文也提到了要把无用的script
去掉,再用re.sub将其替换为string格式,即可得到我们的文本。
将获取的文本写入txt文件
#文件路径
dir_path = "/Users/ouusen/Documents/"
if not os.path.exists(dir_path):
#若无该文件夹,则建立一个文件夹
os.mkdirs(dir_path)
#打开并写入文本
with open(dir_path + "/" + title + ".txt",'a') as f:
f.write(title[0] + '\n')
f.write(text[:-1])
f.close()
open中,'a'
表示打开的时候将指针指向最后,即追加文本。
获取整本小说
# -*- coding: utf-8 -*-
import requests, re, os, time, random
from bs4 import BeautifulSoup
start_time = time.time()
url = "https://www.boquge.com/book/3392/"
headers = {
"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
}
try:
r = requests.get(url, headers = headers)
r.raise_for_status()#get请求的状态,get失败会报错
r.encoding = r.apparent_encoding
except:
print("爬取失败")
dir_path = "/Users/ouusen/Documents/"
if not os.path.exists(dir_path):
os.mkdirs(dir_path)
urls = re.findall('<li ><a href="(.*?)">.*?</a></li>', r.text)
url_source = "https://www.boquge.com"
i = 0;
for url in urls:
url = url_source + url
try:
r = requests.get(url, headers = headers)
r.raise_for_status()
r.encoding = r.apparent_encoding
except:
print("爬取失败")
soup = BeautifulSoup(r.text, "html.parser")
title = re.findall('<div id="h1" class="text-center"><h1>(.*?)</h1><br/>', r.text)
texts = soup.select('#chapter #txtContent')[0]
for ss in texts.select("script"):
#删除无用项
ss.decompose()
#按照指定格式替换章节内容,运用正则表达式
text=re.sub( '\s+', '\r\n\t', texts.text).strip('\r\n')
with open(dir_path + "/" + "最强狂兵.txt",'a') as f:
f.write(title[0] + '\n')
f.write(text[:-1])
f.close()
i += 1
# 每过一段时间就停顿一段时间
if random.random()>0.75:
print("已下载{}%".format( i*100 / len(urls) ) )
time.sleep(2 * random.random())
end_time = time.time()
print("爬取成功,共计用时:{}".format(end_time - start_time))
写在最后
爬取小说的每个章节有两种方法,可以用上述方式,从目录中保存每个章节的链接,也可以从第一个章节开始,提取下一章的链接,第一种的空间复杂度肯定更大,不过时间复杂度就不得而知了。感兴趣的朋友们可以试试修改一下代码,比较一下。不过由于CPU的计算有波动,比较一次是不够的,需要比较多次取平均值才行。
感觉还不错的朋友们点个赞吧。
网友评论