简单爬取本博客 -「又识爬虫」

notebook

又识爬虫

爬虫是一个很实用的技巧,它为我们提供了大量爬取网络上的数据的快捷方便的技巧。现实生活中,爬虫被广泛用于数据分析、数据汇总、AI 训练等等多方面。

并不只是我们在使用爬虫。搜索引擎是如何推荐你的文章到搜索结果页面的?还是爬虫。搜索引擎会根据网站的爬虫协议(一般在网站根路径会有一个 ./robots.txt,例如我的博客的爬虫协议,一个合格的搜索引擎应当根据该协议规范自己的爬虫行为)进行对网站的内容的爬取。

请注意:爬虫学的好,牢饭吃得饱。互联网并非法外之地,你应当爬取只被允许爬取的网站、内容。在对一些网站的内容进行爬虫并商用的行为也有可能是非法的,所以最好不要做出非法爬虫行为。

开始爬虫

扯远了,现在让我们回到爬取我的博客本身吧!

在这里主要分为两个部分进行分析:爬虫部分,将保存的信息摘要保存到 db.json,还有词云部分。

爬虫部分

在此次爬虫中,我主要爬取了主页的每条博客的 URL标题文章摘要 三个部分,数据结构如下:

{
"index": {
"URL": "http://blog.2to.fun/post/xxx/",
"title": "xxxxx", //文章标题
"excerpt": "xxxxx" //文章摘要
}
}

使用的库包括:requestslxmljson

系统环境:Python 3.8.5 on WSL2, Ubuntu @20.04 LTS

爬虫部分新建一个爬虫类:

class Spider:
def __init__(self, url) -> None:
self.url = url
self.headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': 'https://blog.2to.fun/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36 Edg/90.0.818.42'
}

def run(self) -> dict:
response = requests.get(url=self.url, headers=self.headers)
if (statu:=response.status_code) != 200:
raise(f"[-] Connection lost. Status code: {statu}")
response.encoding = 'utf8'
html_content = response.text
html_content_etree = etree.HTML(html_content)
return self.formatting(html_content_etree)

def formatting(self, content):
arc = {}
articles = content.xpath('/html/body/main/div[1]/div/div/div/div/div/article')
for index, item in enumerate(articles):
url = self.url + item.xpath('./h1/a/@href')[0]
post = item.xpath('./h1/a')[0].text
excerpt = item.xpath('./p/a/text()')[0]
arc.update({f"{index}": {"url": url, "post": post, "excerpt": excerpt}})
return arc

当然,对于这个爬虫本身不需要构建到一个类,只是我个人很喜欢封闭类,暴露一个接口,这个见仁见智。

思路总体是很简单的:

  • 首先传入 URL,并调用 run() 方法运行爬虫。
  • run() 过程中,首先请求到网页的全文本信息。
  • 把网页文本格式定义为 utf-8,避免中文出现乱码。
  • 通过 lxml 库的 etree 方法处理文本信息。
  • 使用 xpath 定位到每一篇文章的元素。
  • 返回用过 xpath 找到的 URLtitleexcerpt

接着是开始调用:

blog = Spider(TARGET_URL)
res = blog.run()

并保存到 db.json 中:

with open(file='db.json', mode='w', encoding='utf8') as f:
f.write(json.dumps(res, ensure_ascii=False, indent=4))

在上面爬虫过程中,将保存的数据以字典的形式回传。因此在这里只需要将回传的数据,使用 json.dums() 方法规范,并写入到文件中即可。

需要注意的是在我写代码的时候发觉字典类型出现的是单引号,此时写入的 db.json 本身的语法是错误的。
如果你也出现了这个情况,配置 ensure_ascii=False 即可解决单双引号问题。

词云部分

通过上面的操作,我们已经把我们需要的博客的 URL标题文章摘要 保存到 db.json 中了。接下来,我们需要对该字典读取,切割成为可迭代的单词列表,并进行词云绘制。

怎么实现这些功能呢?

  • jieba 是一个非常好用的中文单词分割 Python 库,可以通过 pip install jieba 安装。

  • wordcloud,正如其名,把多个单词生成一个词云。但是其本身传入的是一长串字符串,以空格分隔、

try:
import jieba
import wordcloud
except ImportError:
raise ModuleNotFoundError
import json

from wordcloud.wordcloud import FONT_PATH

DB_FILE = "./db.json"

content_str = ''


try:
with open(file=DB_FILE, mode='r', encoding='utf8') as f:
file = json.load(f)
except FileNotFoundError:
raise FileNotFoundError

for key, values in file.items():
content_str += values['excerpt'] + ' '

jb = jieba.lcut(content_str)
wc = ''
for item in jb:
wc += item + ' '

wordcloud.WordCloud(font_path='msyh.ttc', background_color=None, width=1080, height=720, mode='RGBA').generate(wc).to_file('result.png')

代码中逻辑还是比较清晰的:

  • 首先读取 db.json 数据文件。
  • 标题、URL 不是我们所期望出现在词云的内容,因此我们选择了 excerpt(文章摘要)
  • 接着使用 jieba 库的精准分割模式,获得可迭代的单词列表。
  • 读取单词列表,以空格为分隔符,拼接列表中的所有单词。
  • 使用 wordcloud.Wordcloud() 方法生成词云并保存到文件。

在最后一步中,font_path 不是必要的,如果生成的词云的中文是乱码的,那你需要在这里指定一个同级目录下支持中文的字体文件。

width、height 是词云的长宽

mode 设定为 RGBA 时,指定 background_colornone 可以实现图片背景透明的效果。

result.png

相关代码

spider.py

wordcloud.py

Author: DioPong

Permalink: https://blog.2to.fun/post/notebook/spide-my-blog/

文章默认使用 CC BY-NC-SA 4.0 协议进行许可,使用时请注意遵守协议。

Comments

Unable to load Disqus, please make sure your network can access.