Здесь черновик кода спайдера с разными (закоментированными) вариантами Rules Linkextractor. Регулярные выражения в правилах и все остальное я далее изменил. Здесь записано, как я "вспоминал" свжеполученные навыки двухнедельной давности:
"Две недели назад я написал краулер-полуфабрикат. Потом неделя ушла на упражнения с XPath и RE, потом неожидано "образовалась" другая срочная работа..."
3. Первый вариант шаблона для подбора и проверки XPath запросов и команды для нормализации строк
2. Делаем Crawler`а из простого (basic) паука carmailPrice Scrapy
1. Срочно пишем...
Regular Expressions Python ... и мой перечень главных команд
Самый краткий справочник по "простейшему" XPath и полсотни
Рассуждения о кустарном призводстве пауков¶
Проблема в том, что интеллигентному слесарю-кустарю приходится все делать самому. Выполнание цепочка рабочих процессов: Постановка задачки -> Подготовка исследования-> -> Инфраструктура для проекта -> Парсинг -> Чистка и подгтовка данных -> Data Mining предполагает не только широкие познания, но и большие перерывы в работе. Например, несколько месяцев назад написал паука, потом занимался обработкой данных и разработкой методик статистического анализа данных. Потом появился новый заказчик, нужно парсить дополнительные сайты, готовить новых пауков. Потом находить "старые" таблицы и отчеты, брать оттуда некоторые столбцы и исследования. Потом нужно формировать новые наборы данных из старых и новых таблиц. Потом проводить предварительную стат обработку, и, наконец добавить понты с Data Mining...
Сегодня у меня подобная ситуация. Две недели назад я написал краулер-полуфабрикат. Потом неделя ушла на упражнения с XPath и RE, потом неожидано "образовалась" другая срочная работа. Прошло ровно две недели. Конечно, маловато для чистоты эксперимента, но кое-что я все-таки забыл...
Сначала вспомним, что было 2 недели назад.¶
Смотрим предыдущий пост на эту тему (№2 в блоке ссылок выше). Ага, вот он - последний спайдер. Помнится, он работал... В этом пауке я парсил локальные html страницы, потому пренебрег всеми настройками... и сосредоточился только на экспериментах с Rule(LinkExtractor...
%load C:\Users\kiss\SkyDrive\Docs\mailru\cars_mail_1\carmailPrice\carmailPrice\spiders\price.py
#from scrapy.selector import Selector
#
#from scrapy.contrib.linkextractors import LinkExtractor
#from scrapy.contrib.spiders import CrawlSpider, Rule
#from carmailPrice.items import CarmailpriceItem
import scrapy
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.contrib.spiders import CrawlSpider, Rule
from carmailPrice.items import CarmailpriceItem
class PriceSpider(CrawlSpider):
name = 'price'
# allowed_domains = ['cars.mail.ru']
start_urls = [#'http://www.cars.mail.ru/',
#'file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/bmw/index.html',
'file:///C:/Users/kiss/Desktop/carsmail/carsmail/index.html',
]
# Rules for selecting URL like this
# 'file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/bmw/3/e92/coupe/index.html'
# 'file://C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/bmw/index.html'
rules = (
#Rule(LinkExtractor(allow=('Desktop/carsmail/carsmail/cars.mail.ru/catalog/bmw/',)), callback='parse_item'),
#Rule(LinkExtractor(allow=('^file.+(?=catalog/bmw/)',r'^file.+(?=catalog/audi/)')), callback='parse_item', follow=False),
#Rule(LinkExtractor(allow=('file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/',), \
# deny=('^file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/\w+/index.html', r'^http.*',)),
# callback='parse_item',follow=False),
Rule(LinkExtractor(allow=('^file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/bmw/index.html',)), \
callback='parse_item',follow=True),
#Rule(LinkExtractor(allow=('^file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/[a-zA-Z0-9_-]+/index.html',)),\
# callback='parse_item',follow=True),
Rule(LinkExtractor(allow=('file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/.+?/.+?/.*index[.]html',),\
deny=(r'^http.*',)), callback='parse_item_2',follow=False),
)
def parse_item_2(self, response):
self.log('A item_2 response from %s just arrived!' % response.url)
#import ipdb; ipdb.set_trace()
def parse_item(self, response):
# self.log('A response from %s just arrived!' % response.url)
# hxs = Selector(response)
firm_list = response.css('.catalog-generation__card')
items = []
for sel in firm_list:
item = CarmailpriceItem()
item['name'] = sel.xpath('.//a[@class="catalog-generation__card__title"]/text()').extract()
item['link'] = sel.xpath('.//a[@class="catalog-generation__card__title"]/@href').extract()
item['price'] = sel.xpath('.//span[@class="rank"]/i/text()').extract()
items.append(item)
return items
Вспоминаем, как работают объекты Rule, LinkExtractor (по памяти)¶
К каждму объекту Response краулер последовательно применяет правила фильтрации ссылок. В каждом правиле указывается, следовть ли по ссыле, или\и вызывать функцию обработки(парсинга) страницы (точнее, объекта Response) callback='parse_item_2',follow=False По умолчанию предполагается, что если есть функция обратного вызова (callback), то follow=False (можно не указвывать). Я попробовал разрешить следование по ссылкам, как видно из кода выше, и мой краулер уполз в дебри сайта. Таким образом, чтобы не ломать голову над тем, куда же может уползти паук, надо взять за правило - либо переход по сылке, либо выполнение функции обратного вызова.
В краулере выше две функции обратного вызова, которые вызываются двумя разными правилами. Помнится, я тогда не представлял себе, как правильно парсить html страницы со множеством иерархических ссылок, потому два правила и опробовал. Экспериентировать с двумя правилами я (наверное) буду позже, а здесь мы используем одну функцию обратного вызова, в которой будут XPath запросы с использованием предков и потомков.
Мои предварительные тренировки c XPath запросами в посте 3. Первый вариант шаблона... Так что будем брать селекторы оттуда.
И не стоит забывать, что в этих же постах-предшественниках я осваивал и команды для чистки спарсенного кода. Но, думаю, правильнее будет не усложнять краулер (спайдер), а написать для чистки отдельный модуль middleware.
И вспомним также, что еще раньше я протестировал паука без Rules, он вырезал три поля в соответствии со списком, так что, сначлча отладим новые правила, а потом начнем добавлять к ним поля XPath
Какими должны брыть правила Rules¶
- Зададим (точнее, оставим без изменений) стрчку
start_urls = \
['file:///C:/Users/kiss/Desktop/carsmail/carsmail/index.html',]
#- это страница со всеми нужными ссыылками,
#ее сконфигурировал предыдущий парсер, который закачал все на локальный диск
На этой странице ничего парсить не будем, но будем загружать вот такие страницы фирм
# С этой странице мы попадаем по ссылке на страниц ФИРМЫ
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/bmw/index.html
Rule(LinkExtractor\
(allow=('^file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/\
[a-zA-Z0-9_-]+/index.html',),\
deny=(r'^http.*',)), follow=True),
В правиле выше все просто, в строке адреса варьируется только один параметр - название фирмы. В посте Делаем Crawler`а из простого... мы уже нашли, что надо использовать [a-zA-Z0-9_-]+
Информация на страницах фирм не нужна, а вот на страницах марок мы и остановимся и дальше не пойдем
# По ссылке со страницы фирм переходим на страницы МАРОК
# Вот та страничка, которую я использовал ранее
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/bmw/3/f30_31/sedan/index.html
#А здесь дела посложнее, после паки фирмы
# следуют три папки модели (марки), модификации, кузова
# Папки может быть и не три? На первый взгляд - нет
# я два варианта - три папки или скоько удгодно папок.
/bmw/
3/f30_31/sedan/
index.html
Неправильный вариант¶
Сначала я написал вот ткой вариант, но оказалось, что этот нежадный поиск работает не так, как я себе представлял, и не разделяет при поиске строку адреса фирмы от других адресов
# Три папки + папка фирмы = 4
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/.+?/.+?/.+?/.+?/index.html
# Более простой вариант
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/.*/index.html
Работающий вариант
Я вспомнил, что в папке утилит есть скрипт для отладки регулярных выражений и после 15 минут экспериментов пришел к выводу, что обманывал себя, полагая, что осовил регулярные выражения. Но в итоге я нашел вариант для того, чтобы находит строки с авдресом, длиннее, чем ...catalog/bmw/index.html
C:\Users\kiss\Anaconda\Tools\Scripts>redemo.py
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/bmw/3/f30_31/sedan/index.html
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/bmw/index.html
# Вот работающий вариант
# находит только первую (длинную) строчку из блока выше
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/(.+?/){2,}index.html
# Если задать {5,}, то и первая строчка не будет отмечена
Не надо бояться дублей¶
Если посмотреть документацию Scrapy LinkExtractor, то тма мы прочитаем, что по умолчанию включена опция unique (boolean) – whether duplicate filtering should be applied to extracted links Так что, пока проще проэкспериментировать, что получится..., а потом уже и подумать, если понадобится.
Итак, получается вот такой код для работы со ссылками¶
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/[a-zA-Z0-9_-]+/index.html
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/(.+?/){2,}index.html
Отметим, что здесь мы используем две идеи для поиска 1) ищем из набора символов, среди которых нет слэша, 2) Ищем наборы которые заканчиваются слэшем.
Не уверен, что это лучшие варианты, напрмер можно было бы использовать наборы, в которых нет слэша:
[^/]
действительно, правильнее вот такой вариант:
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/[a-zA-Z0-9_-]+/index.html
file:///C:/Users/kiss/Desktop/carsmail/carsmail/cars.mail.ru/catalog/([a-zA-Z0-9_-]+/){2,}index.html
Последовательность строго определенных символов, заканчивающаяся слэшем... Надо бы выствить минимальное количество повторов на 4-ку. Но это потом проверим ...
При загрузке страниц с диска подгружается всякая гадость (скрипты javascript) и ссылки на картинки, надо помнить про эут особенность, но с текстом ниже я погорячился... и все же оставил его, чтобы не забыть про Wireshark¶
Осторожно, все страницы с (кроссдоменной) AJAX загрузкой всей центральной части. Причем, сохраненная в локальном компьютере страница при открытии в браузере посылает запрос в интернет и перезаписывает себя....
Я просто отключил соединение с интернет. И, кажется, начинаю понимать, как бороться с "тормозами" браузера... (индексация кэша, обмен данными с поисковиками..., шпионские картинки от mail.ru )
Однако, вернемся к наушему краулеру:
# Ссылка
in2.xpath('//div[@class="catalog-age__mod__item__box"]/a/@href')
def parse_item(self, response):
# self.log('A response from %s just arrived!' % response.url)
# hxs = Selector(response)
firm_list = response.css('.catalog-generation__card')
items = []
for sel in firm_list:
item = CarmailpriceItem()
item['name'] = sel.xpath('.//a[@class="catalog-generation__card__title"]/text()').extract()
item['link'] = sel.xpath('.//a[@class="catalog-generation__card__title"]/@href').extract()
item['price'] = sel.xpath('.//span[@class="rank"]/i/text()').extract()
items.append(item)
return items
Посты чуть ниже также могут вас заинтересовать
Комментариев нет:
Отправить комментарий