爬取百度百科[scrapy启发]
来源:互联网 发布:绝食瘦下来知乎 编辑:程序博客网 时间:2024/05/29 18:53
摘要:主要是基于业务的需要,要一批词,学习了scrapy,借鉴scrapy的一点点思想,写了一个临时爬虫。
一开始,是采用scrapy来写的,可是对于一个框架不熟悉,需要要花时间学习;还有一个主要的,好像代码并不会因为用了这个框架少了多少,可能抓取大量的会有优势。还有一个,我的研究业务单一,就是想要一批词,并且现在就想要,来做一个研究。还有一个, scrapy的异常机制还未找到怎么应用,当出现一些错误它就运行停止了,受制于scrapy。如果把它的源码看完估计用起它会好很多,因为网上的资料都是基础的入门篇,什么代理,什么头配置,什么cookie,什么异常处理都要分别查找。在尝试scrapy之后,未果,不过可把它思想搬过来用一下,然后写了一个临时的任务。
1. 实体类
定义实体类,其实这个是字典,就两个字段,一个词,一个是url;
class BaiduBaikeItem(scrapy.Item): keyword = scrapy.Field() url = scrapy.Fiel)
2. spider
在写scrapy的时候,也用到了yield这个东西,这个是生成器,当程序运行到yield语句时,会返回到next或send或for循环或异常那里的。这个可以查看上一篇《python[变量作用域-函数-闭包-装饰器-生成器]》。
还有一个细节,这里用到一个from urllib.parse import urljoin函数,这个函数可以帮助我们节省很多拼url的代码,这个是在scrapy中发现的。
对于选择器的解释,scrapy里面封装了一个,这里采用了from bs4 import BeautifulSoup这个,这个东西对于HMTL标签解释挺强大的。
对于url请求,采用了requests这个包,它可请求get,post等http协议请求。
保存是把字典转成json,然后把json采用codecs来写文件。
# coding=utf-8import codecsimport jsonimport randomimport timefrom urllib.parse import urljoinimport requestsfrom bs4 import BeautifulSoupfrom items import BaiduBaikeItemfrom crawDataWithProxy import proxy_handler# 根列表cataUrls = { 'http://baike.baidu.com/fenlei/%E5%81%A5%E5%BA%B7%E7%A7%91%E5%AD%A6': '健康科学',}baikeHeaders = { 'Host': 'baike.baidu.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0', 'Accept': '*/*', 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip,deflate', 'Cache-Control': 'no-cache', 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': '0', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1'}# 是否使用代理proxy = False# 重爬次数M = 10COUNT = 0# get responsedef r_get(url, isproxy=False): time.sleep(random.random() * 0.5) r = None try: if not isproxy: r = requests.get(url, timeout=20, headers=baikeHeaders) else: r = requests.get(url, timeout=20, proxies=proxy_handler, cookies=None) except Exception as e: print(e) if (r is not None and r.status_code != 200): r = None return r# 重试def retry(n, url): s = random.random() * (2 * n) + 1 print('%dth times, %f秒后重试。' % (n, s)) time.sleep(s) response = r_get(url, isproxy=proxy) return (response, n + 1)# 解释,这个函数用来递归def myparse(url, page=0, catalog=1): global COUNT n = 0 page += 1 if page == 1: print('##########(catalog:%s(%s),page:%d)#(total itmes:%d)###########' % (cataUrls[url], url, page, COUNT)) else: print('##########(catalog:%d,page:%d(%s)#(total items:%d)###########' % (catalog, page, url, COUNT)) response = r_get(url, isproxy=proxy) # 当抓取失败后,可进行多次抓取 while True: if response == None: rs = retry(n, url) response = rs[0] n = rs[1] if n < M: continue else: print('已经进行了%d次的重试..%s' % (M, url)) break else: response.encoding = 'utf-8' htmls = response.text base = response.url htmlbf = BeautifulSoup(htmls, "lxml") i = 0 try: itemlist = htmlbf.select(".grid-list > ul > li > div.list > a") except Exception as e: print(e) itemlist = [] # 对于空情况,要考虑是否重试 if len(itemlist) == 0: if u'百度百科错误页' in htmlbf.text: print(u'百度百科错误页...') response = None continue else: print('%s request has no items.' % (url)) else: # 解释每个关键词 for quote in itemlist: b_item = BaiduBaikeItem() b_item['keyword'] = quote.text b_item['url'] = urljoin(base, quote['href']) i += 1 print('count of item:%d' % (i)) # 产生item,这个跳出去让这个item进行保存起来,这个scrapy也是这样实现的 yield b_item COUNT = COUNT + i # 找到下一页的网址,让它递归就可以了 print('# 下一页') try: next_sr = htmlbf.select('#next') next_page_url = next_sr[0]['href'] except Exception as e: print('下一页 Exception %s' % (e)) next_page_url = None if next_page_url is not None: print("准备下一页") # 这个算是一个生成器嵌套着一个生成器了,虽然是自已调用了自己 g = myparse(url=urljoin(base, next_page_url), page=page, catalog=catalog) for item in g: yield item else: pass # 如果是第一页的时才会从分类的目录进行前进,找到下一级目录的URL,让它递归 if page == 1: print("准备下一级目录") try: categoryx = htmlbf.select(".p-category > div > span")[0].text except Exception as e: print('has no 下一级目录,e = %s, url = %s' % (e, url)) categoryx = None if categoryx is not None and u'下级分类' in categoryx: next_sr = htmlbf.select(".p-category > div > a") for next_catalog in next_sr: c_url = next_catalog['href'] txt = next_catalog.text if c_url is not None: whole_url = urljoin(base, c_url) # 排除重复的url if whole_url not in cataUrls: cataUrls[whole_url] = txt c_item = BaiduBaikeItem() c_item['keyword'] = txt c_item['url'] = whole_url # 希望对这个分类也保存一下 yield c_item # 开始下一个分类 catalog += 1 print('will claw (%s:%s)' % (txt, c_url)) # 嵌套生成器,遍历时就调用了这个生成器 g = myparse(url=whole_url, page=0, catalog=catalog) for item in g: yield item else: print('(%s,%s) has be crawed..will skip it ' % (txt, c_url)) else: print('this is not 下级分类, skip...') else: pass breakif __name__ == '__main__': file = codecs.open('%s.json' % ('baike'), 'w+', encoding='utf-8') # 这里要用一个副本做存出来,不然后说cataUrls是变的提示 start_urls = list(cataUrls.keys()) for url in start_urls: g = myparse(url) for item in g: # print(item) line = json.dumps(dict(item), ensure_ascii=False) + "\n" file.write(line) file.flush() file.close() for k, v in cataUrls.items(): print(k, v)
3. 代理格式
可以设置一下请求代理,格式如下,像上面那样引用就可以了。
proxyHost = "地址"proxyPort = "端口"proxyUser = "用户名"proxyPass = "密码"proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % { "host": proxyHost, "port": proxyPort, "user": proxyUser, "pass": proxyPass,}proxy_handler = { "http": proxyMeta, "https": proxyMeta,}
本试探性研究到这里,仅作为学习研究参考,才疏深浅,请指教。。
【作者:happyprince, http://blog.csdn.net/ld326/article/details/78757223】
- 爬取百度百科[scrapy启发]
- scrapy+beautifulsoup+mongo数据库简单爬虫——利用搜索关键词爬取百度百科城市地理信息
- Python3 爬取百度百科
- python爬取百度百科
- 利用Scrapy爬取糗事百科段子
- [Scrapy]爬取糗事百科段子
- Python爬虫_BeautifulSoup爬取百度百科
- Python爬取百度百科页面数据
- Python爬虫,爬取百度百科词条
- Python爬虫爬取百度百科词条
- python爬取百度百科词条内容
- java爬取百度百科词条
- Scrapy爬取百度图片(一)
- Scrapy爬取百度图片(二)
- 简单的python爬虫(爬取百度百科词条)
- Python 简单爬虫实现(爬取百度百科信息)
- python3 爬虫学习-根据关键词爬取百度百科内容
- Python爬虫爬取百度百科内容实例
- webrtc中的网络反馈与控制
- POJ1426-Find The Multiple
- 家族 SSL_1896 (并查集)
- java 23种设计模式详解
- ORA-01940:无法删除当前已连接的用户
- 爬取百度百科[scrapy启发]
- 滴滴开源基于 Vue.js 的移动端组件库 cube-ui
- 如何把本地项目上传到Github
- 前端优化
- UDP-server/client实现字符大写转换
- [知了堂学习笔记]_Ajax入门
- Linux多进程编程之在线词典
- 半死不活的seo博客前途在哪?
- 家族 SSL_1896