Пишу первого паука для парсинга XML фида. Сначала я прочитал документацию и попытался разобраться с основными понятиями. Этот процесс отразился в посте "Читаю документацию XMLFeedSpider", а вот здесь попробовал записать процесс работы над пауком. В итоге работающий полуфабрикат.
Читаю документацию XMLFeedSpider
XMLFeedSpider example
Selector.register_namespace
scrapy tool The first thing you typically do with the scrapy tool is create your Scrapy project
How to scrape xml feed with xmlfeedspider
XMLFeedSpider Bscrapy
i['url'] = node.select('link/text()').extract_unquoted()здесь
How to loop over nodes with xmlfeed using scrapy python
how to parse a sitemap.xml file using scrapy's XmlFeedSpider?
Scrapy - Follow RSS links
1.Надо бы вспомнить (на компьютере w8), как создать новыы проект при помощи опций командной строки.¶
scrapy tool The first thing you typically do with the scrapy tool is create your Scrapy project:
C:\Users\kiss\Documents\GitMyScrapy>mkdir scrapy_xml_1
C:\Users\kiss\Documents\GitMyScrapy>cd scrapy_xml_1
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1>scrapy startproject XMLFeedSpider
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1>tree /F
Структура папок
Серийный номер тома: 00000075 6017:2A0B
C:.
└───XMLFeedSpider
│ scrapy.cfg
│
└───XMLFeedSpider
│ items.py
│ pipelines.py
│ settings.py
│ __init__.py
│
└───spiders
__init__.py
Создали структуру папок (в каждой уже есть init.py), теперь генерируем (а чё, подстрочный перевод !) паука
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1>cd XMLFeedSpider
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1\XMLFeedSpider>scrapy genspider proxylists proxylists.net
Created spider 'proxylists' using template 'crawl' in module:
XMLFeedSpider.spiders.proxylists
Зря я поспешил, template 'crawl' высокочил по умолчанию, а мне надо парсить один длиннющий фид, для таких случаев дожен быть другой шаблон ( template), читаем мануал и распечатываем справку:
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1\XMLFeedSpider>scrapy genspider -h
Usage
=====
scrapy genspider [options] <name> <domain>
Generate new spider using pre-defined templates
Options
=======
--help, -h show this help message and exit
--list, -l List available templates
--edit, -e Edit spider after creating it
--dump=TEMPLATE, -d TEMPLATE
Dump template to standard output
--template=TEMPLATE, -t TEMPLATE
Uses a custom template.
--force If the spider already exists, overwrite it with the
template
Global Options
--------------
--logfile=FILE log file. if omitted stderr will be used
--loglevel=LEVEL, -L LEVEL
log level (default: DEBUG)
--nolog disable logging completely
--profile=FILE write python cProfile stats to FILE
--lsprof=FILE write lsprof profiling stats to FILE
--pidfile=FILE write process ID to FILE
--set=NAME=VALUE, -s NAME=VALUE
set/override setting (may be repeated)
--pdb enable pdb on failure
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1\XMLFeedSpider>
# Ага, можно посмотреть список шаблонов
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1\XMLFeedSpider>scrapy genspider -l
Available templates:
basic
crawl
csvfeed
xmlfeed
# Так и есть, нам нужен xmlfeed
#Распечатаем, что мы сгенерировали по умолчанию
%load "C:\\Users\\kiss\\Documents\\GitMyScrapy\\scrapy_xml_1\\XMLFeedSpider\\XMLFeedSpider\\spiders\\proxylists.py"
from scrapy.selector import HtmlXPathSelector
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.contrib.spiders import CrawlSpider, Rule
from XMLFeedSpider.items import XmlfeedspiderItem
class ProxylistsSpider(CrawlSpider):
name = 'proxylists'
allowed_domains = ['proxylists.net']
start_urls = ['http://www.proxylists.net/']
rules = (
Rule(SgmlLinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
def parse_item(self, response):
hxs = HtmlXPathSelector(response)
i = XmlfeedspiderItem()
#i['domain_id'] = hxs.select('//input[@id="sid"]/@value').extract()
#i['name'] = hxs.select('//div[@id="name"]').extract()
#i['description'] = hxs.select('//div[@id="description"]').extract()
return i
Совсем неплохо на первый взгляд, ... здесь есть XmlfeedspiderItem, но это же имя, которое мы сами присвоили пауку. Так что он собирается парсить html страницу, а не фид. Пробуем заменить паука:
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1\XMLFeedSpider>scrapy genspider -t xmlfeed --force proxylists proxylists.net
Created spider 'proxylists' using template 'xmlfeed' in module:
XMLFeedSpider.spiders.proxylists
#Посмотрим, что темерь получилось (напоминаю, нам нужно парсить XML)
%load "C:\\Users\\kiss\\Documents\\GitMyScrapy\\scrapy_xml_1\\XMLFeedSpider\\XMLFeedSpider\\spiders\\proxylists.py"
from scrapy.contrib.spiders import XMLFeedSpider
from XMLFeedSpider.items import XmlfeedspiderItem
class ProxylistsSpider(XMLFeedSpider):
name = 'proxylists'
allowed_domains = ['proxylists.net']
start_urls = ['http://www.proxylists.net/feed.xml']
iterator = 'iternodes' # you can change this; see the docs
itertag = 'item' # change it accordingly
def parse_node(self, response, selector):
i = XmlfeedspiderItem()
#i['url'] = selector.select('url').extract()
#i['name'] = selector.select('name').extract()
#i['description'] = selector.select('description').extract()
return i
Вот именно такой шаблон нам и нужен, он почти совпадает с примером из документации (ниже)
Теперь просто ск4опируем сюда код из XMLFeedSpider example
from scrapy import log
from scrapy.contrib.spiders import XMLFeedSpider
from myproject.items import TestItem
class MySpider(XMLFeedSpider):
name = 'example.com'
allowed_domains = ['example.com']
start_urls = ['http://www.example.com/feed.xml']
iterator = 'iternodes' # This is actually unnecessary, since it's the default value
itertag = 'item'
def parse_node(self, response, node):
log.msg('Hi, this is a <%s> node!: %s' % (self.itertag, ''.join(node.extract())))
item = TestItem()
item['id'] = node.xpath('@id').extract()
item['name'] = node.xpath('name').extract()
item['description'] = node.xpath('description').extract()
return item
Отметим, что наш spider идентичен примеру из документации, и в том и в другом случае при запуске паука вызывается (по умолчанию)метод parse_node(self, response, node), только в нашем пауке вместо идентификатора "node" - "selector", что, естественно, не важно... Более существенное отличие - использование .select вместо .xpath ... С этим разберемся чуть позже.
2. А Далее займемся настройкой своего паука "proxylists"¶
2.1 А как выглядит то, что мы собираемся пасить? proxylists.xml¶
<?xml version="1.0" encoding="iso-8859-1"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specification.html" version="2.0">
<channel>
<title>ProxyLists.Net - leading to privacy</title>
<link>http://www.proxylists.net/</link>
<description>Lists proxies in different countries of different types</description>
<image>
<title>ProxyLists.Net - leading to privacy</title>
<url>http://www.proxylists.net/88x31.gif</url>
<link>http://www.proxylists.net/</link>
</image>
<language>en-us</language>
<lastBuildDate>Tue, 14 Oct 2014 23:21:42 CDT</lastBuildDate>
<ttl>60</ttl>
<managingEditor>support@proxylists.net</managingEditor>
<generator>Proxy RSS Generator v0.1</generator>
<item>
<title>AL proxies on 2014/10/15 04:21:41</title>
<link>http://www.proxylists.net/al_0.html</link>
<description>AL proxies are in the database at the moment.</description>
<guid>http://www.proxylists.net/al_0.html</guid>
<prx:proxy>
<prx:ip>77.242.22.254</prx:ip>
<prx:port>8741</prx:port>
<prx:type>Socks4</prx:type>
<prx:country>Albania</prx:country>
<prx:check_timestamp>10/13 03:36:32</prx:check_timestamp>
</prx:proxy>
</item>
<item>
...
</item>
...
Выше я скопирова фрагмент кода (в предцдущем посте такой же). Здесь есть пространство имен, а значит, надо его указать..., как рекомендовано в мануале XMLFeedSpider.namespaces
class YourSpider(XMLFeedSpider):
# ...
namespaces = [('prx', 'http://www.proxyrss.com/specification.html')]
itertag = 'prx:url'
# ...
#Однако, в нашем фиде есть обычный тег
<item>
# Стот ли использовать вместо него тег в простанстве имен?
<prx:proxy>
Проще попробовать, чем копать документацию...
Тык с наскока №1 Получилось !¶
Если (в режиме iterator = 'iternodes') не строится дерево DOM, а используются регулярные выражения, то начнем с проверки простейшего парсинга:
#Просто добавим пару обычных тегов title link
# и будем парсить локальный файл
%load "C:\\Users\\kiss\\Documents\\GitMyScrapy\\scrapy_xml_1\\XMLFeedSpider\\XMLFeedSpider\\spiders\\proxylists.py"
from scrapy.contrib.spiders import XMLFeedSpider
from XMLFeedSpider.items import XmlfeedspiderItem
class ProxylistsSpider(XMLFeedSpider):
name = 'proxylists'
#allowed_domains = ['proxylists.net']
start_urls = ['file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpider/proxylists.xml']
#instead of online ['http://www.proxylists.net/feed.xml']
iterator = 'iternodes' # you can change this; see the docs
itertag = 'item' # change it accordingly
def parse_node(self, response, selector):
i = XmlfeedspiderItem()
i['title'] = selector.select('title').extract()
i['link'] = selector.select('link').extract()
#i['description'] = selector.select('description').extract()
return i
# И в Items добавим поля title, link
%load "C:\\Users\\kiss\\Documents\\GitMyScrapy\\scrapy_xml_1\\XMLFeedSpider\\XMLFeedSpider\\items.py"
# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html
from scrapy.item import Item, Field
class XmlfeedspiderItem(Item):
# define the fields for your item here like:
# name = Field()
title = Field()
link = Field()
#name = Field()
scrapy crawl proxylists - Теперь запустим все это из консоли, как положено и получим длиннющий список¶
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1\XMLFeedSpider>scrapy crawl proxylists
2014-10-16 15:14:56+0400 [scrapy] INFO: Scrapy 0.20.1 started (bot: XMLFeedSpider)
2014-10-16 15:14:56+0400 [scrapy] DEBUG: Optional features available: ssl, http11, boto, django
2014-10-16 15:14:56+0400 [scrapy] DEBUG: Overridden settings: {'NEWSPIDER_MODULE': 'XMLFeedSpider.spiders', 'SPIDER_MODULES': ['XMLF
eedSpider.spiders'], 'BOT_NAME': 'XMLFeedSpider'}
2014-10-16 15:15:00+0400 [scrapy] DEBUG: Enabled extensions: LogStats, TelnetConsole, CloseSpider, WebService, CoreStats, SpiderState
2014-10-16 15:15:04+0400 [scrapy] DEBUG: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMid
dleware, RetryMiddleware, DefaultHeadersMiddleware, MetaRefreshMiddleware, HttpCompressionMiddleware, RedirectMiddleware, CookiesMid
dleware, ChunkedTransferMiddleware, DownloaderStats
2014-10-16 15:15:04+0400 [scrapy] DEBUG: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlL
engthMiddleware, DepthMiddleware
2014-10-16 15:15:04+0400 [scrapy] DEBUG: Enabled item pipelines:
2014-10-16 15:15:04+0400 [proxylists] INFO: Spider opened
2014-10-16 15:15:04+0400 [proxylists] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2014-10-16 15:15:04+0400 [scrapy] DEBUG: Telnet console listening on 0.0.0.0:6023
2014-10-16 15:15:04+0400 [scrapy] DEBUG: Web service listening on 0.0.0.0:6080
2014-10-16 15:15:04+0400 [proxylists] DEBUG: Crawled (200) <GET file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpide
r/proxylists.xml> (referer: None)
XMLFeedSpider\spiders\proxylists.py:14: ScrapyDeprecationWarning: Call to deprecated function select. Use .xpath() instead.
i['title'] = selector.select('title').extract()
XMLFeedSpider\spiders\proxylists.py:15: ScrapyDeprecationWarning: Call to deprecated function select. Use .xpath() instead.
i['link'] = selector.select('link').extract()
2014-10-16 15:15:05+0400 [proxylists] DEBUG: Scraped from <200 file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpider
/proxylists.xml>
{'link': [u'<link xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specification.
html">http://www.proxylists.net/al_0.html</link>'],
'title': [u'<title xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specificatio
n.html">AL proxies on 2014/10/15 04:21:41</title>']}
2014-10-16 15:15:05+0400 [proxylists] DEBUG: Scraped from <200 file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpider
/proxylists.xml>
{'link': [u'<link xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specification.
html">http://www.proxylists.net/dz_0.html</link>'],
'title': [u'<title xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specificatio
n.html">DZ proxies on 2014/10/15 04:21:41</title>']}
2014-10-16 15:15:05+0400 [proxylists] DEBUG: Scraped from <200 file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpider
/proxylists.xml>
{'link': [u'<link xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specification.
html">http://www.proxylists.net/ar_0.html</link>'],
'title': [u'<title xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specificatio
n.html">AR proxies on 2014/10/15 04:21:41</title>']}
2014-10-16 15:15:05+0400 [proxylists] DEBUG: Scraped from <200 file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpider
/proxylists.xml>
{'link': [u'<link xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specification.
html">http://www.proxylists.net/am_0.html</link>'],
...
...
'title': [u'<title xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specificatio
n.html">Port 64028 proxies on 2014/10/15 04:21:42</title>']}
2014-10-16 15:15:08+0400 [proxylists] INFO: Closing spider (finished)
2014-10-16 15:15:08+0400 [proxylists] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 281,
'downloader/request_count': 1,
'downloader/request_method_count/GET': 1,
'downloader/response_bytes': 907912,
'downloader/response_count': 1,
'downloader/response_status_count/200': 1,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2014, 10, 16, 11, 15, 8, 632000),
'item_scraped_count': 176,
'log_count/DEBUG': 183,
'log_count/INFO': 3,
'response_received_count': 1,
'scheduler/dequeued': 1,
'scheduler/dequeued/memory': 1,
'scheduler/enqueued': 1,
'scheduler/enqueued/memory': 1,
'start_time': datetime.datetime(2014, 10, 16, 11, 15, 4, 815000)}
2014-10-16 15:15:08+0400 [proxylists] INFO: Spider closed (finished)
C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1\XMLFeedSpider>
Результат парсинга каждого item - словарь, в котром справа искомый текст в окружении тегов (title, link), причем в теги (title, link) вставляются атрибуты пространств имен.
{
'link': [u'<link xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specification.html">
http://www.proxylists.net/al_0.html
</link>'],
'title': [u'<title xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:prx="http://www.proxyrss.com/specification.html">
AL proxies on 2014/10/15 04:21:41
</title>']
}
<title>AL proxies on 2014/10/15 04:21:41</title>
<link>http://www.proxylists.net/al_0.html</link>
Если мы теперь (тупо) добавим поле с двоеточием (prx:ip), то получим ошибку¶
Если добавить поле "prx:ip" и в пауке задать еще строчку для парсинга, то команда scrapy crawler выдаст ошибку:
File "XMLFeedSpider\items.py", line 13
prx:ip = Field()
^
yntaxError: invalid syntax
Проверим селектор xpath¶
Если добавить
i['description'] = selector.xpath('description').extract()а не
i['description'] = selector.select('description').extract()
Наблюдаем, что оба варианта селектора работает одинаково, вернее, выдают один и тот же результат...
Вот, как мы научились изменять пространство имен и извлекать текст из тегов¶
# и будем парсить локальный файл
%load "C:\\Users\\kiss\\Documents\\GitMyScrapy\\scrapy_xml_1\\XMLFeedSpider\\XMLFeedSpider\\spiders\\proxylists.py"
from scrapy.contrib.spiders import XMLFeedSpider
from XMLFeedSpider.items import XmlfeedspiderItem
class ProxylistsSpider(XMLFeedSpider):
name = 'proxylists'
#allowed_domains = ['proxylists.net']
start_urls = ['file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpider/proxylists.xml']
#instead of online ['http://www.proxylists.net/feed.xml']
namespaces = [('prx', 'http://www.proxyrss.com/specification.html')]
iterator = 'iternodes' # you can change this; see the docs
itertag = 'item' # change it accordingly
def parse_node(self, response, selector):
i = XmlfeedspiderItem()
i['title'] = selector.select('title').extract()
# i['link'] = selector.select('link').extract()
#i['descr'] = selector.xpath('description').extract()
i['xmlprx'] = selector.xpath("//prx:ip/text()").extract()
return i
Естественно, мы добавили соответствующие поля и в items.py (предыдущий см. In [4]), новый вариант публиковать не будем (экономим место). Обратим внимание, что по сравнению с предыдущим вариантом паука (выше см. In [3]) ... мы изменили (по сути) две строчки:
#Очевидно, что надо использовать namespaces
namespaces = [('prx', 'http://www.proxyrss.com/specification.html')]
# и учить xpath - xpath("//prx:ip/text()")
i['xmlprx'] = selector.xpath("//prx:ip/text()").extract()
Итак, все основные задачи распарсивания фида решены. Далее предстоит решить вопрос, стоил ли поменять
itertag = 'item'
# на <prx:proxy> напрмер
# поскольку оказалось, что в <item> может находится несколько (десяток) <prx:proxy>
Об этом еще предстоит подумать, очевидно, что все будет зависеть от конкретных реализаций пауков.
Как обрабатывать информацию дальше?¶
Пока я склоняюсь к подходу pipeline. Далее из большого файла можно выбрать (отфильтровать) страну, вид прокси.... Фильтровать можно и по времени... Очевидно, что фильтры можно делать как можно проще, чтобы они были универсальными.... Последним должен быть pipeline записи в файл. Надо предусмотреть разные варианты записи в разных форматах.
Посты чуть ниже также могут вас заинтересовать
Комментариев нет:
Отправить комментарий