tigerspider是一个用于抓取网页以及解析出有用信息的爬虫框架.
虽然tigerspider是为了抓取网页设计的爬虫框架,但是同样适用于从APIS(如Amazon Associates Web Services)提取数据.
这篇文档的目的是为了向您介绍tigerspider的基本使用,使得您可以使用tigerspider完成基本的数据抓取任务.
如果你准备好了,就可以开始阅读这篇文档了.
运行tigerspider所需要的库有:
- tornado
- redis
- docopt
- psycopg2
- requests
如果你有setuptools,你可以如下方式安装所有的库:
easy_install tornado
现在你想从一个网页中提取需要的数据,但是网站没有提供可用的APIS. 那么tigerspider可以帮助你抓取需要的数据.
假设现在我们需要从一个网页中提取一些需要的信息,我们需要如何做。 我们试着抓取一个网页的内容:
http://www.228.com.cn/ticket-49052202.html
如果这个网页失效了,你可以选择http://www.228.com.cn中任一个票务信息,进行抓取.
第一步是定义你所要提取出来的数据。在tigerspider中,这是通过Item来实现的. 这将会是我们定义的item:
from tigerspider.core.datastruct import Item class WebItem(Item): """描述活动详情页提取出来的数据 """ def __init__(self, url, order, description, time_info, price, name): self.url = url # url self.order = order # 订票电话 self.description = description # 详情介绍 self.time_info = time_info # 时间信息 self.price = price # 价格 self.name = name # 名字
接下来我们介绍如何实现一个解析器,用于提取网页的内容.
网页的url,比较容易获得,就是你访问的url地址.
我们使用Xpath来提取网页的html信息. 我们可以观察一下我们需要提取的description, picture_path, time_info, price,都可以通过xpath比较容易的提取出来.
查看html代码,可以发现name是在 <div class="product-price-titleul">
中:
<div class="product-price-titleul"> <h1>2014曲婉婷say the words 我为你歌唱 **巡回演唱会 <span class="product-price-zt"> </h1> ...
可以通过一个xpath语句得到name信息:
//div[@clas='product-price-titleul']/h1/text()
我们可以查看html代码,我们可以注意到 <li class="tel">
:
<li class="tel"> 订票电话: <span>4006-228-228</span> </li>
那么我们可以通过一个Xpath表达式来提取出电话:
//div[@class='top-w']//li[@class='tel']/span/text()
同时价格是在 <div class="productnew-header-pricec2 clearfloat">
tag中的:
<div class="productnew-header-pricec2 clearfloat"> <ul id="z_price", class="productnew-header-pricec2-ul productnew-header-pricec3-ul productnew-header-pricec2-cq"> <li type="price"> <span>180</span> </li> ....
我们可以通过一个xpath来提取price:
//ul[@class='productnew-header-pricec2-ul productnew-header-pricec3-ul productnew-header-pricec2-cq']/li/@title
时间信息,也可以看到是再 <ul>
中:
<ul id="z_date" class="productnew-header-pricea2-ul clearfloat"> <li class="choose" type="date" d="2014-05-30 19:30"> ...
所以我们也可以通过xpath获得时间信息:
//ul[@class='productnew-header-pricea2-ul clearfloat']/li/@d
最后description是在 <div>
标签中:
<div class="product-detail-alla-cont" style="line-height:26px;margin-top:12px;"> ...
我们可以使用一个xpath获得:
//div[@class='product-detail-alla-cont']
如果需要详细了解XPath,请访问 http://www.w3school.com.cn/xpath/index.asp
最终的解析代码如下:
from lxml import html from tigerspider.core.util import flist from tigerspider.core.spider.parser import BaseParser from tigerspider.spiders.intro1.items import WebItem class ActivityParser(BaseParser): """用于解析活动详情页面的解析器 """ def __init__(self, namespace): BaseParser.__init__(self, namespace) self.logger.info(u"init Activity Parser finished") def parse(self, task, input_file): """详情解析器 Args: task, HttpTask, 任务 input_file: file, 网页文件 Yields: item: WebItem, 数据 task: HttpTask, 新任务 """ tree = html.parse(input_file) name = flist(tree.xpath(u"//div[" u"@clas='product-price-titleul']/h1/text()")) desc_elems = tree.xpath(u"//div[@class='product-detail-alla-cont']") description = _extract_desc_elems(desc_elems) date_elems = tree.xpath( u"//ul[@class='productnew-header-pricea2-ul clearfloat']/li/@d") telephone = flist(tree.xpath( u"//div[@class='top-w']//li[@class='tel']/span/text()")) telephone = telephone.replace(u"-", u"") if len(telephone) == 0: telephone = u"4006228228" price_elems = tree.xpath( u"//ul[@class='productnew-header-pricec2-ul productnew-" u"header-pricec3-ul productnew-header-pricec2-cq']/li/@title") price_infos = list() for price_elem in price_elems: if unicode(price_elem) not in price_infos: price_infos.append(unicode(price_elem)) price_info = u"/".join(price_infos) time_infos = [] for date_elem in date_elems: time_infos.append(date_elem) time_info = u";".join(time_infos) url = task.request.url # 保存详情信息 yield WebItem(url, telephone, description, time_info, price_info, name) def _extract_desc_elems(desc_elems): """extract description Args: desc_elems: list, [Elment] Returns: description: unicode, description """ texts = [] for desc_elem in desc_elems: for text in desc_elem.itertext(): texts.append(text.strip()) return u"".join(texts)
在tigerspider中,是通过pipeline来处理解析出来的结果的. pipeline捕获到解析器中yield出来的对象,并进行处理。以下我们就将解析出来的结果保存到csv文件中. 我们定义的WebItemPipeline如下:
import csv from tigerspider.core.spider.pipeline import BasePipeline from tigerspider.spiders.intro1.items import WebItem class WebItemPipeline(BasePipeline): def __init__(self, namespace, out_path=u"/home/wuyadong/webitem.csv"): BasePipeline.__init__(self, namespace) self._out_file = open(out_path, u"wb") self._csv_file = csv.writer(self._out_file) self.logger.info(u"init WebItemPipeline finish") def process_item(self, item, kwargs): """process web item Args: item: WebItem """ if isinstance(item, WebItem): no_unicode = lambda a: a.encode(u'utf-8') if isinstance( a, unicode) else a url = no_unicode(item.url) order = no_unicode(item.order) description = no_unicode(item.description) time_info = no_unicode(item.time_info) price = no_unicode(item.price) name = no_unicode(item.name) self._csv_file.writerow([name, url, order, price, time_info, description]) def clear_all(self): self._out_file.close()
我们需要用一个Spider类组织齐需要的解析器,和处理器,来描述一个Spider的基本组成,我们是通过Spider来实现的:
from tornado.httpclient import HTTPRequest from tigerspider.core.spider.spider import BaseSpider from tigerspider.core.datastruct import HttpTask from tigerspider.spiders.intro1.parser import ActivityParser from tigerspider.spiders.intro1.pipeline import WebItemPipeline class Intro1Spider(BaseSpider): parsers = { u"ActivityParser": ActivityParser, } pipelines = { u"WebItem": WebItemPipeline, } start_tasks = [HttpTask(HTTPRequest( u"http://www.228.com.cn/ticket-49052202.html"), callback=u"ActivityParser")]
start_tasks是一个描述开始任务的列表,pipelines和parsers是放置对应的解析器和处理器的类对象
最后,我们将会启动爬虫,去抓取数据,并将解析出来的数据以csv格式保存到本地文件中。
首先,我们要在settings/registersettings注册一下对应的spider:
spiders = ['spiders.intro1.spider.Intro1Spider']
启动monitor进程:
python monitor.py
通过api接口启动抓取worker,在浏览器输入url:
http://127.0.0.1:1235/api/start_worker?schedule_path=schedules.schedules.RedisSchedule&spider_path=spiders.intro1.spider.Intro1Spider&schedule_interval=1000&schedule_max_number=1
这样,稍等片刻就会有数据输出,可以看到webitem.csv中存放的数据