导语

pyspider是一个基于python的网络爬虫框架,提供了强大的WebUI界面,支持Mysql,MongoDB,PostgreSQL等多种数据库,并且支持Redis,RabbitMQ做消息队列。


最近的项目需要用pyspider来抓取clinicaltrials上的数据,在这里总结一下

安装

安装pip
wget https://bootstrap.pypa.io/get-pip.py
python get-pip.py
遇到报错
Could not find a version that satisfies the requirement Flask>=0.10 (from pyspider)
这是没有安装Flask导致的,pip install flask
编译过程中报错 Failed building wheel for lxml,说明缺少lxml库
先查看yum源中的python库信息,yum list|grep python
找到包含lxml的那一项
yum install python-lxml.x86_64
然后即可顺利安装pyspider 安装pyspider
pip install pyspider
运行
pyspider
打开*http://localhost:5000/*即可进入pyspider的控制台

根据关键字爬取PubMed的数据


爬虫的原理是从某个URL开始,遍历这个URL所指向页面中包含的所有链接,然后逐一对请求这些链接,分析返回过来的页面数据,根据关键字抓取相关字段,如果这个页面中也包含链接那么继续打开,并继续做分析抓取,直到所请求的页面中不再包含链接

找出起始URL以及分页URL

打开clinicaltrials输入一个关键字,比如ovarian cancer,然后再出来的列表页里最下方会有一个分页链接,我们要将ovarian cancer 所有相关信息都获取到,所以必须将每个分页都遍历到

右键审查元素,查看一下分页链接的特点 image

可以看到链接的title为Show next page of results,但是在页面中有两个这样的链接,但是我们只需要其中的一个即可,要实现这功能最方便的就是css选择器,好在pyspider引入了PyQuery,PyQuery相当于把jquery用python实现了一遍。按照pyquery的语法可以这样实现
a[title^="Show next page of results"]').eq(0).attr.href

爬取指定的链接

一个页面里面会有很多链接,而需要爬取的页面链接往往具有某种规律,右键审查元素观察规律
发现链接是由**https://www.clinicaltrials.gov/ct2/show/**加一串字母或数字构成,所以可以通过正则表达式来匹配这一类链接
引入正则表达式的库 import re
编写函数

def index_page(self, response):   
	for each in response.doc('a[href^="http"]').items():
        if re.match("https://www.clinicaltrials.gov/ct2/show/\w+", each.attr.href):
            self.crawl(each.attr.href, callback=self.detail_page)
    self.crawl(response.doc('a[title^="Show next page of results"]').eq(0).attr.href,callback=self.index_page)
抓取指定的内容

页面上的内容都包含在HTML元素中,只需唯一匹配这些元素即可,pyspider中内置的是PyQuery,它是一个用python实现的HTML页面遍历的库,语法类似于Jquery中的元素遍历

"title" : response.doc('h1').text(),
"condition": response.doc('td.body3').eq(0).text(),
"intervention": response.doc('td.body3').eq(1).text(),
"identifier": response.doc('.identifier').text()

doc()方法用于在整个页面查找
eq(n)方法用于在结果集中选择第n个元素,类似于数组用法array[n]
text()方法用于获取html标签内的文本

用一个类封装一下以上的方法就可以用来工作了

import re
from pyspider.libs.base_handler import *
class Handler(BaseHandler):
	crawl_config = {
	}
	@every(minutes=24 * 60)
	def on_start(self):
    	self.crawl('http://www.clinicaltrials.gov/ct2/results?term=ovarian+cancer&recr=&rslt=&type=&cond=&intr=drug&titles=&outc=&spons=&lead=&id=&state1=&cntry1=&state2=&cntry2=&state3=&cntry3=&locn=&gndr=&phase=1&phase=2&rcv_s=&rcv_e=&lup_s=&lup_e=', callback=self.index_page)

	@config(age=10 * 24 * 60 * 60)
	def index_page(self, response):
    	for each in response.doc('a[href^="http"]').items():
        	if re.match("https://www.clinicaltrials.gov/ct2/show/\w+", each.attr.href):
            	self.crawl(each.attr.href, callback=self.detail_page)
    	self.crawl(response.doc('a[title^="Show next page of results"]').eq(0).attr.href,callback=self.index_page)
	@config(priority=2)
	def detail_page(self, response):
    	return {
        	"url": response.url,
        	"title" : response.doc('h1').text(),
        	"condition": response.doc('td.body3').eq(0).text(),
        	"intervention": response.doc('td.body3').eq(1).text(),
        	"identifier": response.doc('.identifier').text()
    	}

index_page函数用来抓取页面中所需抓取的那一类链接,detail_page方法用来分析某个链接的页面,抓取其中的文字