这篇文章将为大家详细讲解有关大规模异步新闻爬虫中如何实现一个更好的网络请求函数,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
downloader 的实现
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 = 'http://news.baidu.com/' s, html = downloader(url) print(s, len(html))
这个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()来输出异常,有助于我们发现更多问题。
关于“大规模异步新闻爬虫中如何实现一个更好的网络请求函数”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。