在博客网站发布了一些博客。总共有四十多篇。现在查看这些博客需要翻页,不是很方便。
想要弄出一个列表,把标题和链接展示出来。如果手动去复制粘贴,耗时耗力,也不够自动化。
有没有什么自动化的方法呢?想到以前用python做过抓取的功能。
开发环境:
- Python3.7
- PyCharm 2018.3.7 (Community Edition)
- macOS 11.4
安装scrapy
我们主要使用的框架是scrapy,官网 https://scrapy.org/
如果需要换用清华的pip源,执行下面的命令
1 2
| pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pip -U pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
|
根据官网文档进行安装
分析目标网页
目标网页的部分html如下
1 2 3 4 5
| <div class="blog-menu blog-menu-noImg"> <div class="blog-menu-header blog_no_attachment"> <a id="portal_usercenter_2_usernew_blog_title_cus_i_0" href="/blogs/301790" class="common-blog-title" title="Kotlin协程取消与超时" target="_blank" style="margin-left: 0px; color: rgb(51, 51, 51);"><span class="blog-title-wrap" style="overflow-wrap: break-word;"> Kotlin协程取消与超时 </span></a></div>
|
需要提取的是标题(title)和网址(url)。shell工具
使用scrapy shell
工具来分析一下目标网页
1
| scrapy shell 'https://bbs.huaweicloud.com/community/usersnew/id_1606985929124732/page_1'
|
windows下的url需要用双引号可以看到如下的运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 2021-10-13 09:46:47 [asyncio] DEBUG: Using selector: KqueueSelector [s] Available Scrapy objects: [s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc) [s] crawler <scrapy.crawler.Crawler object at 0x7fd889766990> [s] item {} [s] request <GET https://bbs.huaweicloud.com/community/usersnew/id_1606985929124732/page_1> [s] response <200 https://bbs.huaweicloud.com/community/usersnew/id_1606985929124732/page_1> [s] settings <scrapy.settings.Settings object at 0x7fd88987bb50> [s] spider <DefaultSpider 'default' at 0x7fd889c7b710> [s] Useful shortcuts: [s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed) [s] fetch(req) Fetch a scrapy.Request and update local objects [s] shelp() Shell help (print this help) [s] view(response) View response in a browser 2021-10-13 09:46:48 [asyncio] DEBUG: Using selector: KqueueSelector
|
尝试用scrapy的css()
方法来提取元素,操作它的response
变量
例如提取div.blog-menu
1 2 3 4
| In [2]: response.css('div.blog-menu') Out[2]: [<Selector xpath="descendant-or-self::div[@class and contains(concat(' ', normalize-space(@class), ' '), ' blog-menu ')]" data='<div class="blog-menu blog-menu-noImg"><'>, ...
|
可以得到页面上所有满足要求的元素。拿到的元素是Selector,可以再进行下一步的操作。用for循环读取提取到的元素,在此基础上,去抓取中文标题
1 2 3 4 5 6 7 8 9 10 11 12 13
| In [6]: for item in response.css('div.blog-menu'): ...: print(item.css('a.common-blog-title::attr(title)').extract()[0]) ...: Kotlin协程取消与超时 Android Kotlin协程入门 Kotlin协程基础 Kotlin协程入门 Android View post 方法 Android Activity 传递Parcelable对象 Android Handler,Looper与MessageQueue使用与分析 Android线程池使用介绍 Java线程介绍 使用ECharts绘制网址径向树状图
|
a.common-blog-title::attr(title)
表示的是我们要查找<a>
里面的内容,指定class为common-blog-title
,并且查找title
属性。
css().extract()
将结果列表提取出来。最后得到我们关心的信息。
获取url
同理,把提取部分修改为::attr(href)
代码
前面用shell工具进行分析,我们了解提取目标信息的方法。接下来写Python代码。
在合适的地方新建一个scrapy工程
1
| scrapy startproject blog
|
可以得到一系列文件
1 2 3 4 5 6 7 8 9
| blog spiders .gitignore __init__.py __init__.py items.py middlewares.py pipelines.py settings.py
|
在spiders
目录里新建一个文件Hw.py
,它就是我们的“蜘蛛”。完整代码如下。
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
| import json
import scrapy
def cmp(item): return item['page']
class HwBlog(scrapy.Spider): """ 在最外层的blog目录进行操作 scrapy crawl hw """ name = "hw" res_list = [] req_count = 0 total_url = 5
def start_requests(self): self.res_list = [] urls = [] base_url = 'https://bbs.huaweicloud.com/community/usersnew/id_1606985929124732/page_' for i in range(1, self.total_url + 1): urls.append(base_url + str(i))
for url in urls: yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response): self.req_count = self.req_count + 1 page = response.url.split("_")[-1]
for item in response.css('div.blog-menu'): title_se = item.css("a.common-blog-title") blog_url = 'https://bbs.huaweicloud.com' + (title_se.css("::attr(href)").extract()[0]) blog_title = title_se.css("::attr(title)").extract()[0] self.res_list.append({"title": blog_title, "url": blog_url, "page": page})
json_res = json.dumps(self.res_list, ensure_ascii=False) print(json_res)
res_file_path = 'raw/hw.json' with open(res_file_path, 'w+') as f: f.write(json_res)
if self.req_count < self.total_url: return res_md = 'raw/hw.md' self.res_list.sort(key=cmp) with open(res_md, 'w+') as f: f.writelines('## 华为云社区') f.write('\n博客数量:' + str(len(self.res_list)) + '\n') for d in self.res_list: f.write('\n') f.write('- [' + d['title'] + '](' + d['url'] + ')')
|
要启动的话,输入以下命令
接下来简单看一下代码
变量
name
是蜘蛛的名字。我们启动的时候会用到它。这个名字由我们自己定义。
res_list
是暂存结果的列表。把获取到的结果暂时存放在里面。
total_url
表示一共要爬几个页面
req_count
表示请求次数
start_requests(self)
在这个方法里进行一些配置工作。比如配置目标url。
我们是预先知道有5个页面,所以配置了5个url。
用scrapy.Request(url=url, callback=self.parse)
构建请求
parse
是回调函数,请求成功后走这个方法。
parse(self, response)
请求成功后,结果保存在response
中。前面用shell进行的操作,可以直接写成python代码。
当请求次数达到目标后,开始写文件。
小结
这是Python scrapy的一次简单应用。
安装scrapy,分析目标网页。把获取到的数据整理后写成markdown文件。
视频在这里