02-实现一个更好的网络请求函数
你好,我是悦创。
上一节我们实现了一个简单的再也不能简单的新闻爬虫,这个爬虫有很多槽点,估计大家也会鄙视这个爬虫。上一节最后我们讨论了这些槽点,现在我们就来去除这些槽点来完善我们的新闻爬虫。
问题我们前面已经描述清楚,解决的方法也有了,那就废话不多讲,代码立刻上(Talk is cheap, show me the code!)。
downloader 的实现
# -*- coding: utf-8 -*-
# @Time : 2023/1/18 08:28
# @Author : AI悦创
# @FileName: demo.py
# @Software: PyCharm
# @Blog :https://bornforthis.cn/
import requests
import cchardet
import traceback
def downloader(url, timeout=10, headers=None, debug=False, binary=False):
_headers = {
'User-Agent': ('Mozilla/5.0 (compatible; MSIE 9.0; '
'Windows NT 6.1; Win64; x64; Trident/5.0)'),
}
redirected_url = url
if headers:
_headers = headers
try:
r = requests.get(url, headers=_headers, timeout=timeout)
if binary:
html = r.content
else:
encoding = cchardet.detect(r.content)['encoding']
html = r.content.decode(encoding)
status = r.status_code
redirected_url = r.url
except:
if debug:
traceback.print_exc()
msg = 'failed download: {}'.format(url)
print(msg)
if binary:
html = b''
else:
html = ''
status = 0
return status, html, redirected_url
if __name__ == '__main__':
url = 'https://news.baidu.com/'
s, html, last_url = downloader(url)
print(s, len(html), last_url)
这个 downloader()
函数,内置了默认的 User-Agent
模拟成一个 IE9 浏览器,同时接受调用者自定义的 headers 和 timeout。使用 cchardet
来处理编码问题,返回数据包括:
- 状态码:如果出现异常,设置为 0
- 内容: 默认返回 str 内容。但是URL链接的是图片等二进制内容时,注意调用时要设
binary=True
- 重定向 URL: 有些 URL 会被重定向,最终页面的 url 包含在响应对象里面
新闻 URL 的清洗
我们先看看这两个新闻网址:
http://xinwen.eastday.com/a/n181106070849091.html?qid=news.baidu.com
http://news.ifeng.com/a/20181106/60146589_0.shtml?_zbs_baidu_news
上面两个带?的网站来自百度新闻的首页,这个问号?的作用就是告诉目标服务器,这个网址是从百度新闻链接过来的,是百度带过来的流量。但是它们的表示方式不完全一样,一个是 qid=news.baidu.com
, 一个是 _zbs_baidu_news
。这有可能是目标服务器要求的格式不同导致的,这个在目标服务器的后台的浏览统计程序中可能用得到。
然后去掉问号 ?
及其后面的字符,发现它们和不去掉指向的是相同的新闻网页。
从字符串对比上看,有问号和没问号是两个不同的网址,但是它们又指向完全相同的新闻网页,说明问号后面的参数对响应内容没有任何影响。
正在抓取新闻的大量实践后,我们发现了这样的规律:
新闻类网址都做了大量 SEO,它们把新闻网址都静态化了,基本上都是以 .html
, .htm
, .shtml
等结尾,后面再加任何请求参数都无济于事。
但是,还是会有些新闻网站以参数 id 的形式动态获取新闻网页。
那么我们抓取新闻时,就要利用这个规律,防止重复抓取。由此,我们实现一个清洗网址的函数。
g_bin_postfix = set([
'exe', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
'pdf',
'jpg', 'png', 'bmp', 'jpeg', 'gif',
'zip', 'rar', 'tar', 'bz2', '7z', 'gz',
'flv', 'mp4', 'avi', 'wmv', 'mkv',
'apk',
])
g_news_postfix = [
'.html?', '.htm?', '.shtml?',
'.shtm?',
]
def clean_url(url):
# 1. 是否为合法的http url
if not url.startswith('http'):
return ''
# 2. 去掉静态化url后面的参数
for np in g_news_postfix:
p = url.find(np)
if p > -1:
p = url.find('?')
url = url[:p]
return url
# 3. 不下载二进制类内容的链接
up = urlparse.urlparse(url)
path = up.path
if not path:
path = '/'
postfix = path.split('.')[-1].lower()
if postfix in g_bin_postfix:
return ''
# 4. 去掉标识流量来源的参数
# badquery = ['spm', 'utm_source', 'utm_source', 'utm_medium', 'utm_campaign']
good_queries = []
for query in up.query.split('&'):
qv = query.split('=')
if qv[0].startswith('spm') or qv[0].startswith('utm_'):
continue
if len(qv) == 1:
continue
good_queries.append(query)
query = '&'.join(good_queries)
url = urlparse.urlunparse((
up.scheme,
up.netloc,
path,
up.params,
query,
'' # crawler do not care fragment
))
return url
清洗 url 的方法都在代码的注释里面了,这里面包含了两类操作:
- 判断是否合法 url,非法的直接返回空字符串
- 去掉不必要的参数,去掉静态化 url 的参数
网络爬虫知识点
1. URL 清洗
网络请求开始之前,先把url清洗一遍,可以避免重复下载、无效下载(二进制内容),节省服务器和网络开销。
2. cchardet 模块
该模块是 chardet 的升级版,功能和 chardet 完全一样,用来检测一个字符串的编码。由于是用 C 和 C++ 实现的,所以它的速度非常快,非常适合在爬虫中用来判断网页的编码。
切记,不要相信 requests 返回的 encoding,自己判断一下更放心。上一节,我们已经列举了一个例子来证明 requests 对编码识别的错误,如果忘了的话,可以再去回顾一下。
3. traceback 模块
我们写的爬虫在运行过程中,会出现各种异常,而且有些异常是不可预期的,也不知道它会出现在什么地方,我们就需要用 try 来捕获异常让程序不中断,但是我们又需要看看捕获的异常是什么内容,由此来改善我们的爬虫。这个时候,就需要 traceback 模块。
比如在 downloader()
函数里面我们用 try 捕获了 get()
的异常,但是,异常也有可能是 cchardet.detect()
引起的,用 traceback.print_exc()
来输出异常,有助于我们发现更多问题。
本篇我们讲了编写一个更好的网络请求函数,下一篇我们讲:编写一个好用的 URL Pool
欢迎关注我公众号:AI悦创,有更多更好玩的等你发现!
公众号:AI悦创【二维码】
AI悦创·编程一对一
AI悦创·推出辅导班啦,包括「Python 语言辅导班、C++ 辅导班、java 辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发、Linux、Web」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。当然,还有线下线上摄影课程、Photoshop、Premiere 一对一教学、QQ、微信在线,随时响应!微信:Jiabcdefh
C++ 信息奥赛题解,长期更新!长期招收一对一中小学信息奥赛集训,莆田、厦门地区有机会线下上门,其他地区线上。微信:Jiabcdefh
方法一:QQ
方法二:微信:Jiabcdefh
- 0
- 0
- 0
- 0
- 0
- 0