使用Python语言作为工具进行web数据爬取是一件很轻松的事。正则表达式则是一种可以高速匹配文本的操作,当正则表达式与Python相结合时,也会摩擦出不一样的火花。
本文将通过一个简单的爬虫案例对简书页面进行数据爬取,运用requests模块与re模块。抓取简书首页1000条文章链接与标题。
依赖库
该案例需要导入以下3个Python模块。
- requests
- fake_useragent
- re
其中, requests和fake_useragent并不是Python标准库的模块,使用前可以通过pip安装一下。
pip install requests -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
pip install fake_useragent -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
当然,requests也可以使用urllib代替,主要用来请求页面并返回数据。
fake_useragent的功能为自动生成一个user-agent,当然也可使用自己浏览器的user-agent。
re模块则是Python标准库的正则模块,在本案例中用于过滤数据,关于re模块的使用可以参考笔者之前的文章。
import re
import requests
import fake_useragent
寻找接口
笔者推荐使用Chrome浏览器进行接口排查。
首先进入在Chrome中进入简书首页,按f12进入开发者工具状态,并找到Network选项。
由于页面通过滚轮滑动实现了加载数据,所以笔者认为这是通过Ajax进行了数据渲染。
在Network下有一个XHR的选项,这个选项便是页面中出现的所有Ajax接口。
对爬虫工程师而言,最敏感的单词之一莫过于page,即:页码。没错,在Headers里的Request URL的链接便是我们需要的接口。
构建请求头
关于请求头,我们需要构建:
- User-Agent:防止被检测为爬虫程序
- X-Requested-With:Ajax请求头
- Cookie:添加Cookie是为了爬取推送给本人账号的数据
# 实例化UserAgent对象
user_agent = fake_useragent.UserAgent(use_cache_server=False)
url = 'https://www.jianshu.com/?seen_snote_ids%5B%5D=66169577&seen_snote_ids%5B%5D=70078184&seen_snote_ids%5B%5D=59133070&seen_snote_ids%5B%5D=71131220&seen_snote_ids%5B%5D=69717831&seen_snote_ids%5B%5D=71082246&seen_snote_ids%5B%5D=69512409&seen_snote_ids%5B%5D=66364233&seen_snote_ids%5B%5D=68425069&seen_snote_ids%5B%5D=65829398&seen_snote_ids%5B%5D=70390517&seen_snote_ids%5B%5D=70715611&seen_snote_ids%5B%5D=60025426&seen_snote_ids%5B%5D=69454619&page={}'
# 请求头
headers = {
# 随机生成1个user-agent
'User-Agent': user_agent.random,
'X-Requested-With': 'XMLHttpRequest',
'Cookie': 'xxxxxxx',
}
# 设置代理(可省略)
proxies = {
'http': 'http://54.241.121.74:3128',
'https': 'http://54.241.121.74:3128'
}
需要注意,url链接的page属性使用了{}进行后续页码的传递。
请求页面
首先我们先构建1个全局变量num用来对提取的数据进行计数。
num = 1
接下来便是请求页面了。
# 初始页码
count = 0
# 循环请求页面
while True:
response = requests.get(
# 传入页码
url=url.format(i),
headers=headers,
proxies=proxies
)
count += 1
# 解析页面
title_and_link = get_data(response.text)
# 展示数据
show_data(title_and_link)
# 如果judge为True,代表已经爬取了1000条数据
if num > 1000:
break
解析页面
def get_data(text):
"""
提取页面源码中的信息
:param text: 页面源代码
:return: result
"""
# 生成正则对象
pattern = re.compile(r'<a class="title" target="_blank" href="(.*?)">(.*?)</a>', re.S)
# 查找符合上一行正则对象的数据
result = pattern.finditer(text)
return result
展示数据
def show_data(title_and_link):
"""
展示数据
:param title_and_link: 数据
:return: None
"""
# 重新声明num
global num
for i in title_and_link:
# 打印数据
print(f'{num}:https://www.jianshu.com{i.group(1).strip()}', end=' ')
print(i.group(2).strip())
num += 1
if num > 1000:
print('完成任务!!!')
break
结语
这个爬虫程序还是比较简单的,毕竟找到接口,就可以一步步的深挖数据。
实际上,这个爬虫程序存在着一些比较显而易见的bug,尤其是当你没有设置Cookie的时候。
没错,由于接口原因,当我们没有设置Cookie时,爬取的数据基本上是重复的,并且Cookie也并不能完美的解决这个问题。当数据量较大时,即使设置了Cookie也会出现重复的数据。
这个bug最直接的解决方案便是更换接口,但这样做也代表着需要重构部分代码(虽然也没多少代码)。确实,通过Chrome的开发者工具查看,其他的接口也是存在的,主要看你能不能找到罢了。此外,使用selenium也是一个不错的选择。此处也体现了爬虫程序的主观性,即实现功能的方法是多种多样的。不过笔者想说的是,既然提到了去重,那怎么能少了Redis呢?
没错,下篇文章便是对上述的爬虫程序进行去重的优化操作。
关于本文的完整代码,可以来笔者的GitHub下获取。
https://github.com/macxin123/spider/blob/master/jianshu/re_jianshu.py