С помощью Scrapy мы можем легко завершить написание поискового робота. Однако, если количество просканированных сайтов очень велико, например, при сканировании новостной информации из основных СМИ, несколько пауков могут содержать много повторяющихся кодов.
Если мы сохраняем общедоступные части поисковых роботов каждого сайта и извлекаем различные части в виде отдельных конфигураций, таких как правила сканирования, методы синтаксического анализа страниц и т. д., которые извлекаются и помещаются в файл конфигурации, то при добавлении поискового робота вы нужно только реализовать правила сканирования и правила извлечения этих веб-сайтов.
В этом разделе мы рассмотрим реализацию универсального сканера Scrapy.
1. Ползающий паук
Перед внедрением обычного поискового робота нам нужно узнать о CrawlSpider, ссылка на официальную документацию которого: http://scrapy.readthedocs.io/en/latest/topics/spiders.html#crawlspider.
CrawlSpider — это универсальный паук, предоставляемый Scrapy. В Spider мы можем указать некоторые правила сканирования для извлечения страницы, эти правила сканирования представлены специальной структурой данных Rule. Правило содержит конфигурацию извлечения и последующих страниц.Паук будет определять, какие ссылки на текущей странице необходимо продолжать сканировать в соответствии с правилом, и какие методы необходимо использовать для анализа результатов сканирования каких страниц. .
CrawlSpider
унаследовано отSpider
своего рода. КромеSpider
Все методы и свойства класса, он также предоставляет очень важное свойство и метод.
rules
, который представляет собой атрибут правила сканирования, содержащий один или несколькоRule
список объектов. каждыйRule
Определены действия для сканирования веб-сайтов, и CrawlSpider их прочитает.rules
каждый изRule
и проанализировано.parse_start_url()
, который является переопределяемым методом. когдаstart_urls
Этот метод вызывается, когда соответствующий запрос получает ответ, он анализирует ответ и должен вернутьItem
объект илиRequest
объект.
Самое главное здесьRule
определено, его определение и параметры следующие:
class scrapy.contrib.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
Далее будут описаны по очередиRule
параметр.
-
link_extractor
: объект Link Extractor. Через него паук может узнать, какие ссылки извлечь из просканированных страниц. Извлеченная ссылка автоматически сгенерирует запрос. Это также структура данных, обычно используемаяLxmlLinkExtractor
Объект как параметр, его определение и параметры следующие:class scrapy.linkextractors.lxmlhtml.LxmlLinkExtractor(allow=(), deny=(), allow_domains=(), deny_domains=(), deny_extensions=None, restrict_xpaths=(), restrict_css=(), tags=('a', 'area'), attrs=('href', ), canonicalize=False, unique=True, process_value=None, strip=True)
allow
представляет собой регулярное выражение или список регулярных выражений, который определяет, какие ссылки, извлеченные с текущей страницы, соответствуют требованиям, и только те ссылки, которые соответствуют требованиям, будут обработаны.deny
Напротив.allow_domains
Определено доменное имя, отвечающее требованиям, и только ссылка этого доменного имени будет использована для создания нового запроса, что эквивалентно белому списку доменных имен.deny_domains
Напротив, это эквивалентно черному списку доменных имен.restrict_xpaths
Определяет извлечение ссылок из соответствующей области XPath на текущей странице, и его значением является выражение XPath или список выражений XPath.restrict_css
Определяет извлечение ссылок из областей на текущей странице, которые соответствуют селекторам CSS и значением которых является селектор CSS или список селекторов CSS. Есть и другие параметры, представляющие метку извлеченной ссылки, необходимость удаления дубликатов, обработка ссылки и т. д., которые используются нечасто. Вы можете обратиться к описанию параметров документа: http://scrapy.readthedocs.io/en/latest/topics/link-extractors.html#module-scrapy.linkextractors.lxmlhtml. callback
: То есть функция обратного вызова и ранее определенный Requestcallback
имеют одно и то же значение. каждый раз отlink_extractor
Эта функция будет вызываться при получении ссылки в . Функция обратного вызова получаетresponse
в качестве первого аргумента и возвращает список, содержащий объекты Item или Request. Обратите внимание, избегайте использованияparse()
как функция обратного вызова. так какCrawlSpider
использоватьparse()
способ реализовать свою логику, еслиparse()
метод переопределен,CrawlSpider
не удастся.cb_kwargs
: словарь, содержащий аргументы, переданные функции обратного вызова.follow
: логическое значение, т.е.True
илиFalse
, что указывает на то, что по правилу изresponse
Нужно ли отслеживать извлеченную ссылку. еслиcallback
ПараметрыNone
,follow
По умолчанию установлено значениеTrue
, иначе по умолчаниюFalse
.process_links
: указывает функцию обработчика, начиная сlink_extractor
Эта функция будет вызываться при получении связанного списка из , и в основном используется для фильтрации.process_request
: Это также назначенная функция обработки. Когда каждый запрос извлекается в соответствии с правилом, эта функция вызывается для обработки запроса. Функция должна вернутьRequest
илиNone
.
Приведенный выше контент является основным использованием основного правила в CrawlSpider. Но этого может быть недостаточно для завершения работы сканера CrawlSpider. Затем мы используем CrawlSpider для реализации примера сканирования новостного веб-сайта, чтобы лучше понять использование Rule.
2. Загрузчик предметов
Мы узнали об использовании правила CrawlSpider для определения логики сканирования страницы, что является частью настраиваемости. Однако Rule не определяет правил извлечения элементов. Для извлечения Item нам нужно использовать другой модуль Item Loader.
Загрузчик элементов предоставляет удобный механизм, помогающий нам легко извлекать элементы. Он предоставляет ряд API-интерфейсов, которые могут анализировать необработанные данные для присвоения значений элементам. Item предоставляет контейнер для хранения очищенных данных, а Item Loader предоставляет механизм для заполнения контейнера. С ним извлечение данных становится более упорядоченным.
API загрузчика элементов выглядит следующим образом:
class scrapy.loader.ItemLoader([item, selector, response, ] **kwargs)
API загрузчика элементов возвращает новый загрузчик элементов для заполнения данного элемента. Если Item не указан, используемый класс создается автоматически.default_item_class
. Также проходит вselector
иresponse
параметры для создания экземпляров с помощью селекторов или параметров ответа.
Параметры API загрузчика элементов будут подробно описаны ниже.
item
:этоItem
предмет, который можно назватьadd_xpath()
,add_css()
илиadd_value()
и другие способы заполненияItem
объект.selector
:этоSelector
Объект, селектор, используемый для извлечения данных заполнения.response
:этоResponse
Объект, используемый для создания ответа селектора.
Типичный экземпляр загрузчика элементов выглядит следующим образом:
from scrapy.loader import ItemLoader
from project.items import Product
def parse(self, response):
loader = ItemLoader(item=Product(), response=response)
loader.add_xpath('name', '//div[@class="product_name"]')
loader.add_xpath('name', '//div[@class="product_title"]')
loader.add_xpath('price', '//p[@id="price"]')
loader.add_css('stock', 'p#stock]')
loader.add_value('last_updated', 'today')
return loader.load_item()
Здесь сначала объявите элемент продукта, используйтеItem
иResponse
создание экземпляра объектаItemLoader
,перечислитьadd_xpath()
метод извлекает данные из двух разных мест и присваивает ихname
собственность, повторное использованиеadd_xpath()
,add_css()
,add_value()
и другие методы по очереди присваивают значения разным свойствам и, наконец, вызываютload_item()
Метод реализует разбор Item. Этот способ более регулярен, некоторые параметры и правила мы можем извлекать отдельно и оформлять в конфигурационные файлы или хранить в базе данных для достижения настраиваемости.
Кроме того, каждое поле Item Loader содержит процессор ввода (процессор ввода) и процессор вывода (процессор вывода). Процессор ввода извлекает данные, как только он их получает, а результаты процессора ввода собираются и сохраняются в загрузчике элементов, но не присваиваются элементу. Собрав все данные,load_item()
метод вызывается для заполнения регенерацииItem
объект. При выполнении вызова сначала будет вызван обработчик вывода для обработки данных, собранных ранее, а затем сохраненных в элементе, таким образом, создается элемент.
Некоторые из встроенных процессоров описаны ниже.
1. Identity
Identity
Это самый простой процессор, который не выполняет никакой обработки и возвращает исходные данные напрямую.
2. TakeFirst
TakeFirst
Возвращает первое ненулевое значение списка, напримерextract_first()
Функция, обычно используемая в качестве процессора вывода, выглядит следующим образом:
from scrapy.loader.processors import TakeFirst
processor = TakeFirst()
print(processor(['', 1, 2, 3]))
Результат выглядит следующим образом:
1
Результат, обработанный этим процессором, возвращает первое ненулевое значение.
3. Join
Join
метод эквивалентен строкеjoin()
метод, вы можете объединить список в строку, и по умолчанию строка разделена пробелами, как показано ниже:
from scrapy.loader.processors import Join
processor = Join()
print(processor(['one', 'two', 'three']))
Результат выглядит следующим образом:
one two three
Он также может изменить разделитель по умолчанию по параметру, например, изменить на запятую:
from scrapy.loader.processors import Join
processor = Join(',')
print(processor(['one', 'two', 'three']))
Результат запуска следующий:
one,two,three
4. Compose
Compose
Процессор создается с заданной комбинацией нескольких функций, каждое входное значение передается первой функции, а его выходные данные передаются второй функции и так далее, пока последняя функция не вернет выходные данные всего процессора, как показано ниже. :
from scrapy.loader.processors import Compose
processor = Compose(str.upper, lambda s: s.strip())
print(processor(' hello world'))
Результат запуска следующий:
HELLO WORLD
Здесь мы создаем Compose Processor, передавая строку с начальным пробелом. Compose Processor имеет два параметра: первыйstr.upper
, которая переводит все буквы в верхний регистр; вторая — анонимная функция, вызывающаяstrip()
метод удаления начальных и конечных пробельных символов.Compose
Два параметра вызываются последовательно, и результирующая строка, наконец, преобразуется в верхний регистр, а начальные пробелы удаляются.
5. MapCompose
иCompose
похожий,MapCompose
Список входных значений можно обрабатывать итеративно следующим образом:
from scrapy.loader.processors import MapCompose
processor = MapCompose(str.upper, lambda s: s.strip())
print(processor(['Hello', 'World', 'Python']))
Результат запуска следующий:
['HELLO', 'WORLD', 'PYTHON']
Обрабатываемый контент представляет собой итерируемый объект,MapCompose
Объект проходится и обрабатывается последовательно.
6. SelectJmes
SelectJmes
Вы можете запросить JSON, передать ключ и вернуть значение, полученное из запроса. Однако вам необходимо установить библиотеку Jmespath, прежде чем вы сможете ее использовать.Команда выглядит следующим образом:
pip3 install jmespath
После установки Jmespath процессор можно использовать следующим образом:
from scrapy.loader.processors import SelectJmes
proc = SelectJmes('foo')
processor = SelectJmes('foo')
print(processor({'foo': 'bar'}))
Результат запуска следующий:
bar
Вышеупомянутое содержимое представляет собой некоторые часто используемые процессоры.В примерах в этом разделе мы будем использовать процессоры для обработки данных.
Далее мы используем пример, чтобы понять использование загрузчика элементов.
3. Задачи этого раздела
Давайте возьмем новости технологии CDC в качестве примера, чтобы понять использование CrawlSpider и Item Loader, а затем извлечем их настраиваемую информацию, чтобы добиться настраиваемости. Ссылка на официальный сайт: http://tech.china.com/. Нам нужно просканировать содержание новостей о технологиях, ссылка: http://tech.china.com/articles/, страница показана ниже.
Мы хотим получить информацию о новостях всех страниц в списке новостей, включая заголовок, текст, время, источник и другую информацию.
4. Новые проекты
Сначала создайте новый проект Scrapy с именем scrapyuniversal следующим образом:
scrapy startproject scrapyuniversal
Чтобы создать CrawlSpider, вам нужно сначала разработать шаблон. Сначала мы можем посмотреть, какие шаблоны доступны, команда выглядит следующим образом:
scrapy genspider -l
Результат запуска следующий:
Available templates:
basic
crawl
csvfeed
xmlfeed
Когда мы создавали Паука раньше, мы использовали первый шаблон по умолчанию.basic
. На этот раз для создания CrawlSpider вам нужно использовать второй шаблон.crawl
, команда создания выглядит так:
scrapy genspider -t crawl china tech.china.com
После запуска будет создан CrawlSpider со следующим содержимым:
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class ChinaSpider(CrawlSpider):
name = 'china'
allowed_domains = ['tech.china.com']
start_urls = ['http://tech.china.com/']
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
def parse_item(self, response):
i = {}
#i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
#i['name'] = response.xpath('//div[@id="name"]').extract()
#i['description'] = response.xpath('//div[@id="description"]').extract()
return i
На этот раз сгенерированный контент Spider является еще однимrules
Определение свойств. Первый параметр правилаLinkExtractor
, как указано вышеLxmlLinkExtractor
, только с другим именем. В то же время функция обратного вызова по умолчанию больше неparse
, ноparse_item
.
5. Определите правила
Для реализации новостей, ползающих, все, что нам нужно сделать, это определить правило, а затем реализовать функцию анализа. Давайте пройдем через этот шаг за шагом.
Первое местоstart_urls
Модифицированный на стартовую ссылку, код выглядит следующим образом:
start_urls = ['http://tech.china.com/articles/']
После этого Паук ползетstart_urls
каждая ссылка внутри. Таким образом, первая просканированная здесь страница — это ссылка, которую мы только что определили. После получения ответа паук будет извлекать гиперссылки на странице в соответствии с каждым правилом для создания дальнейших запросов. Затем нам нужно определить правило, чтобы указать, какие ссылки извлекать.
Текущая страница показана ниже.
Это страница списка новостей, и следующим шагом будет извлечение ссылок на детали каждой новости в списке. Здесь вы можете напрямую указать область, где расположены эти ссылки. Глядя на исходный код, все ссылки есть в IDleft_side
внутри узла, точнее его внутриclass
заcon_item
узла, как показано на следующем рисунке.
Здесь мы можем использоватьLinkExtractor
изrestrict_xpaths
указать атрибут, после чего Паук извлечет все гиперссылки из этой области и сгенерирует Запрос. Однако в навигации по каждой статье могут быть и другие теги гиперссылок, и мы просто хотим извлечь нужные ссылки на новости. Настоящий путь ссылки на новость начинается сarticle
Вначале мы используем регулярное выражение, чтобы сопоставить его и присвоить егоallow
параметры. Кроме того, страницы, соответствующие этим ссылкам, на самом деле являются соответствующими страницами сведений о новостях, и нам нужно проанализировать подробную информацию о новостях, поэтому здесь необходимо указать функцию обратного вызова.callback
.
Теперь мы можем построить правило, код выглядит следующим образом:
Rule(LinkExtractor(allow='article\/.*\.html', restrict_xpaths='//div[@id="left_side"]//div[@class="con_item"]'), callback='parse_item')
Далее нам также нужно сделать так, чтобы текущая страница реализовывала функцию пейджинга, поэтому нам также нужно извлечь ссылку на следующую страницу. Проанализировав исходный код веб-страницы, можно обнаружить, что ссылка на следующую страницу находится в узле с идентификатором pageStyle, как показано на следующем рисунке.
Однако разница между узлом следующей страницы и другими ссылками на страницы невелика.Чтобы удалить эту ссылку, мы можем напрямую использовать метод сопоставления текста XPath, поэтому здесь мы напрямую используемLinkExtractor
изrestrict_xpaths
атрибут для указания извлеченной ссылки. Кроме того, нам не нужно извлекать детали страницы, соответствующие этой ссылке на пейджинг, например, страницу сведений о новостях, то есть нам не нужно генерировать Item, поэтому нам не нужно добавлятьcallback
параметр. Кроме того, если страница на следующей странице прошла успешно, ее необходимо продолжить анализировать, как в приведенной выше ситуации, поэтому также необходимо добавитьfollow
ПараметрыTrue
, представитель продолжает следить за анализом соответствия. фактически,follow
Параметр также можно опустить, т.к.callback
когда пусто,follow
По умолчаниюTrue
. Здесь Правило определяется следующим образом:
Rule(LinkExtractor(restrict_xpaths='//div[@id="pageStyle"]//a[contains(., "下一页")]'))
а сейчасrules
становится:
rules = (
Rule(LinkExtractor(allow='article\/.*\.html', restrict_xpaths='//div[@id="left_side"]//div[@class="con_item"]'), callback='parse_item'),
Rule(LinkExtractor(restrict_xpaths='//div[@id="pageStyle"]//a[contains(., "下一页")]'))
)
Затем запускаем код, команда следующая:
scrapy crawl china
Теперь, когда реализованы перелистывание страниц и подробное сканирование страниц, мы реализуем эту функцию только путем определения двух правил, и эффект операции показан на следующем рисунке.
6. Проанализируйте страницу
Следующее, что нам нужно сделать, это проанализировать содержимое страницы и извлечь заголовок, время публикации, текст и источник. Сначала определите Item следующим образом:
from scrapy import Field, Item
class NewsItem(Item):
title = Field()
url = Field()
text = Field()
datetime = Field()
source = Field()
website = Field()
Поля здесь относятся к заголовку новости, ссылке, тексту, времени выпуска, источнику и названию сайта, где название сайта напрямую назначено CDC. Поскольку это сканер общего назначения, для сканирования новостного контента других сайтов с такой же структурой должно быть много сканеров, поэтому необходимо поле для различения имени сайта.
Предварительный просмотр страницы сведений показан ниже.
Если вы извлекаете содержимое, как и раньше, просто вызовитеresponse
Переменнаяxpath()
,css()
Дождитесь метода. здесьparse_item()
Реализация метода выглядит так:
def parse_item(self, response):
item = NewsItem()
item['title'] = response.xpath('//h1[@id="chan_newsTitle"]/text()').extract_first()
item['url'] = response.url
item['text'] = ''.join(response.xpath('//div[@id="chan_newsDetail"]//text()').extract()).strip()
item['datetime'] = response.xpath('//div[@id="chan_newsInfo"]/text()').re_first('(\d+-\d+-\d+\s\d+:\d+:\d+)')
item['source'] = response.xpath('//div[@id="chan_newsInfo"]/text()').re_first('来源:(.*)').strip()
item['website'] = '中华网'
yield item
Таким образом мы извлекаем информацию о каждой новости и формируем объект NewsItem.
На этом этапе мы фактически завершили извлечение предмета. Снова запустите паука следующим образом:
scrapy crawl china
Выходное содержимое показано на рисунке ниже.
Теперь мы можем успешно извлечь информацию о каждой новости.
Однако мы обнаружили, что это извлечение было очень нерегулярным. Далее мы используемItem Loader
,пройти черезadd_xpath()
,add_css()
,add_value()
и другие способы извлечения конфигурации. мы можем переписатьparse_item()
,Следующее:
def parse_item(self, response):
loader = ChinaLoader(item=NewsItem(), response=response)
loader.add_xpath('title', '//h1[@id="chan_newsTitle"]/text()')
loader.add_value('url', response.url)
loader.add_xpath('text', '//div[@id="chan_newsDetail"]//text()')
loader.add_xpath('datetime', '//div[@id="chan_newsInfo"]/text()', re='(\d+-\d+-\d+\s\d+:\d+:\d+)')
loader.add_xpath('source', '//div[@id="chan_newsInfo"]/text()', re='来源:(.*)')
loader.add_value('website', '中华网')
yield loader.load_item()
Здесь мы определяемItemLoader
подкласс, названныйChinaLoader
, реализация которого выглядит так:
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, Join, Compose
class NewsLoader(ItemLoader):
default_output_processor = TakeFirst()
class ChinaLoader(NewsLoader):
text_out = Compose(Join(), lambda s: s.strip())
source_out = Compose(Join(), lambda s: s.strip())
ChinaLoader
наследоватьNewsLoader
класс, который определяет универсальныйOut Processor
заTakeFirst
, что эквивалентно ранее определенномуextract_first()
функция метода. мы вChinaLoader
определено вtext_out
иsource_out
поле. Здесь используется Compose Processor, который имеет два параметра: первый параметрJoin
Это также процессор, который может объединять список в строку, второй параметр — анонимная функция, которая может удалять символы пробела в начале и в конце строки. После этой серии операций мы преобразуем результат извлечения в виде списка в строку, дедупликацию начальных и конечных пробельных символов.
Код запускается повторно, и эффект извлечения точно такой же.
Пока мы реализовали полууниверсальную конфигурацию обходчика.
Семь, извлечение общей конфигурации
Почему сейчас он только полууниверсальный? Если нам нужно расширить другие сайты, нам все равно нужно создать новый CrawlSpider, определить правило этого сайта и реализовать его отдельно.parse_item()
метод. Также много повторяющегося кода, например, имена переменных и методов CrawlSpider почти одинаковы. Так можем ли мы использовать код нескольких похожих поисковых роботов, извлекать совершенно разные места и превращать их в настраиваемые файлы?
Конечно. Итак, какие части мы можем извлечь? Все переменные могут быть извлечены, напримерname
,allowed_domains
,start_urls
,rules
Ждать. Эти переменные могут быть назначены при инициализации CrawlSpider. Мы можем создать нового общего паука для достижения этой функции, команда выглядит следующим образом:
scrapy genspider -t crawl universal universal
Этот новый паук называетсяuniversal
. Затем мы извлекаем свойства только что написанного паука и настраиваем его в JSON, называем его china.json, помещаем в папку configs и располагаем рядом с папкой пауков.Код выглядит следующим образом:
{
"spider": "universal",
"website": "中华网科技",
"type": "新闻",
"index": "http://tech.china.com/",
"settings": {
"USER_AGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"
},
"start_urls": [
"http://tech.china.com/articles/"
],
"allowed_domains": [
"tech.china.com"
],
"rules": "china"
}
первое полеspider
то есть имя паука, вот оноuniversal
. Ниже приводится описание сайта, например, название сайта, тип, домашняя страница и т. д. последующийsettings
специфичен для этого паукаsettings
Конфигурация, если вы хотите переопределить глобальный проект, конфигурацию в settings.py можно настроить для него отдельно. Далее следуют некоторые свойства Spider, такие какstart_urls
,allowed_domains
,rules
Ждать.rules
Его также можно определить как отдельный файл rules.py и превратить в файл конфигурации для разделения правил, как показано ниже:
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import Rule
rules = {
'china': (
Rule(LinkExtractor(allow='article\/.*\.html', restrict_xpaths='//div[@id="left_side"]//div[@class="con_item"]'),
callback='parse_item'),
Rule(LinkExtractor(restrict_xpaths='//div[@id="pageStyle"]//a[contains(., "下一页")]'))
)
}
Таким образом, мы извлечем базовую конфигурацию. Если вы хотите запустить краулер, вам нужно только прочитать файл конфигурации и динамически загрузить его в паук. Итак, нам нужно определить метод для чтения этого файла JSON следующим образом:
from os.path import realpath, dirname
import json
def get_config(name):
path = dirname(realpath(__file__)) + '/configs/' + name + '.json'
with open(path, 'r', encoding='utf-8') as f:
return json.loads(f.read())
Определенныйget_config()
После метода нам просто нужно передать ему имя файла конфигурации JSON, чтобы получить эту информацию о конфигурации JSON. Затем мы определяем входной файл run.py и помещаем его в корневую директорию проекта, Его функция — запустить Spider, как показано ниже:
import sys
from scrapy.utils.project import get_project_settings
from scrapyuniversal.spiders.universal import UniversalSpider
from scrapyuniversal.utils import get_config
from scrapy.crawler import CrawlerProcess
def run():
name = sys.argv[1]
custom_settings = get_config(name)
# 爬取使用的Spider名称
spider = custom_settings.get('spider', 'universal')
project_settings = get_project_settings()
settings = dict(project_settings.copy())
# 合并配置
settings.update(custom_settings.get('settings'))
process = CrawlerProcess(settings)
# 启动爬虫
process.crawl(spider, **{'name': name})
process.start()
if __name__ == '__main__':
run()
Текущая записьrun()
. Сначала получите параметры командной строки и назначьте их какname
,name
Это имя файла JSON, которое на самом деле является именем целевого веб-сайта для сканирования. Мы сначала используемget_config()
метод, передайте имя, чтобы прочитать только что определенный файл конфигурации. Используйте сканированиеspider
имя в конфигурационном файлеsettings
конфигурации, а затем получитьsettings
Конфигурация и глобальный проектsettings
Конфигурации объединены. Создайте новый CrawlerProcess и передайте конфигурацию, используемую для сканирования. перечислитьcrawl()
иstart()
способ начать сканирование.
существуетuniversal
, мы создаем новый__init__()
метод инициализации конфигурации, реализация выглядит следующим образом:
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapyuniversal.utils import get_config
from scrapyuniversal.rules import rules
class UniversalSpider(CrawlSpider):
name = 'universal'
def __init__(self, name, *args, **kwargs):
config = get_config(name)
self.config = config
self.rules = rules.get(config.get('rules'))
self.start_urls = config.get('start_urls')
self.allowed_domains = config.get('allowed_domains')
super(UniversalSpider, self).__init__(*args, **kwargs)
def parse_item(self, response):
i = {}
return i
существует__init__()
метод,start_urls
,allowed_domains
,rules
и другие свойства назначаются. в,rules
Свойство также считывает конфигурацию rules.py, так что базовая конфигурация сканера успешно реализована.
Затем выполните следующую команду, чтобы запустить сканер:
python3 run.py china
Программа сначала прочитает файл конфигурации JSON, назначит пауку некоторые свойства в конфигурации, а затем начнет сканирование. Эффект операции точно такой же, и результат операции показан на следующем рисунке.
Теперь у нас есть возможность настройки основных свойств Паука. Остальную часть синтаксического анализа также необходимо настраивать.Первоначальная функция синтаксического анализа выглядит следующим образом:
def parse_item(self, response):
loader = ChinaLoader(item=NewsItem(), response=response)
loader.add_xpath('title', '//h1[@id="chan_newsTitle"]/text()')
loader.add_value('url', response.url)
loader.add_xpath('text', '//div[@id="chan_newsDetail"]//text()')
loader.add_xpath('datetime', '//div[@id="chan_newsInfo"]/text()', re='(\d+-\d+-\d+\s\d+:\d+:\d+)')
loader.add_xpath('source', '//div[@id="chan_newsInfo"]/text()', re='来源:(.*)')
loader.add_value('website', '中华网')
yield loader.load_item()
Нам также необходимо извлечь эти конфигурации. Переменные здесь в основном включают в себя выбор класса Item Loader,Item
Для выбора класса и определения параметров метода Item Loader мы можем добавить следующее в файл JSONitem
Конфигурация:
"item": {
"class": "NewsItem",
"loader": "ChinaLoader",
"attrs": {
"title": [
{
"method": "xpath",
"args": [
"//h1[@id='chan_newsTitle']/text()"
]
}
],
"url": [
{
"method": "attr",
"args": [
"url"
]
}
],
"text": [
{
"method": "xpath",
"args": [
"//div[@id='chan_newsDetail']//text()"
]
}
],
"datetime": [
{
"method": "xpath",
"args": [
"//div[@id='chan_newsInfo']/text()"
],
"re": "(\\d+-\\d+-\\d+\\s\\d+:\\d+:\\d+)"
}
],
"source": [
{
"method": "xpath",
"args": [
"//div[@id='chan_newsInfo']/text()"
],
"re": "来源:(.*)"
}
],
"website": [
{
"method": "value",
"args": [
"中华网"
]
}
]
}
}
определено здесьclass
иloader
свойства, которые представляют классы, используемые Item и Item Loader соответственно. Определенныйattrs
свойства для определения правил извлечения для каждого поля, например,title
Каждый определенный термин содержитmethod
атрибут, который представляет используемый метод извлечения, напримерxpath
То есть это означает, что Item Loader вызываетсяadd_xpath()
метод.args
параметры, то естьadd_xpath()
Второй параметр, выражение XPath. противdatetime
поле, мы также использовали обычное извлечение, поэтому здесь мы также можем определитьre
параметр для передачи регулярного выражения, используемого при извлечении.
Мы также будем динамически загружать эти конфигурации вparse_item()
метод. Наконец, самое главное – добитьсяparse_item()
метод следующим образом:
def parse_item(self, response):
item = self.config.get('item')
if item:
cls = eval(item.get('class'))()
loader = eval(item.get('loader'))(cls, response=response)
# 动态获取属性配置
for key, value in item.get('attrs').items():
for extractor in value:
if extractor.get('method') == 'xpath':
loader.add_xpath(key, *extractor.get('args'), **{'re': extractor.get('re')})
if extractor.get('method') == 'css':
loader.add_css(key, *extractor.get('args'), **{'re': extractor.get('re')})
if extractor.get('method') == 'value':
loader.add_value(key, *extractor.get('args'), **{'re': extractor.get('re')})
if extractor.get('method') == 'attr':
loader.add_value(key, getattr(response, *extractor.get('args')))
yield loader.load_item()
Здесь сначала получите информацию о конфигурации элемента, а затемclass
конфигурации, инициализируйте его, инициализируйте загрузчик элементов и просмотрите свойства элемента для извлечения по очереди. судитьmethod
поле, вызовите соответствующий метод обработки для обработки. какmethod
заcss
, просто вызовите загрузчик элементовadd_css()
метод извлечения. После того, как все конфигурации будут динамически загружены, вызовитеload_item()
метод извлечения элемента.
Повторно запустите программу, и результат показан на рисунке ниже.
Результат бега точно такой же.
давайте оглянемся назадstart_urls
Конфигурация. здесьstart_urls
Можно настроить только определенные ссылки. Если таких ссылок 100, 1000, мы не можем перечислить все ссылки, не так ли? При определенных обстоятельствах,start_urls
Также требуется динамическая конфигурация. мы будемstart_urls
Он делится на два типа, один из которых предназначен для непосредственной настройки списка URL-адресов, а другой - для вызова метода для генерации, они соответственно определяются какstatic
иdynamic
тип.
в этом примереstart_urls
очевидноstatic
типа, такstart_urls
Перезапись конфигурации выглядит так:
"start_urls": {
"type": "static",
"value": [
"http://tech.china.com/articles/"
]
}
еслиstart_urls
Он генерируется динамически, и мы можем вызвать метод для передачи параметров следующим образом:
"start_urls": {
"type": "dynamic",
"method": "china",
"args": [
5, 10
]
}
здесьstart_urls
определяется какdynamic
тип в видеurls_china()
, а затем передайте параметры 5 и 10, чтобы создать ссылки для страниц с 5 по 10. Таким образом, нам нужно только реализовать этот метод и создать новый файл urls.py, как показано ниже:
def china(start, end):
for page in range(start, end + 1):
yield 'http://tech.china.com/articles/index_' + str(page) + '.html'
Другие сайты могут настроить сами. Например, для некоторых ссылок необходимо использовать временные метки, параметры шифрования и т. д., которые можно реализовать с помощью пользовательских методов.
Далее в Пауке__init__()
метод,start_urls
Конфигурация переписывается следующим образом:
from scrapyuniversal import urls
start_urls = config.get('start_urls')
if start_urls:
if start_urls.get('type') == 'static':
self.start_urls = start_urls.get('value')
elif start_urls.get('type') == 'dynamic':
self.start_urls = list(eval('urls.' + start_urls.get('method'))(*start_urls.get('args', [])))
Судя здесьstart_urls
Типы обрабатываются по-разному, так что мы можем достичьstart_urls
настроен.
До сих пор настройки Spider, начальные ссылки, свойства и методы извлечения были полностью настраиваемыми.
Таким образом, конфигурация всего проекта включает в себя следующее.
spider
: указывает имя используемого паука.settings
: информация о конфигурации может быть настроена специально для Spider, что переопределит конфигурацию на уровне проекта.start_urls
: укажите начальную ссылку для обхода сканером.allowed_domains
: сайты, сканирование которых разрешено.rules
: Правила сканирования сайта.item
: Правила извлечения данных.
Мы внедрили универсальный сканер Scrapy, и каждому сайту нужно только изменить файл JSON, чтобы получить бесплатную конфигурацию.
Восемь, этот код раздела
Кодовый адрес этого раздела: https://github.com/Python3WebSpider/ScrapyUniversal.
9. Заключение
В этом разделе описывается реализация универсального сканера Scrapy. Мы извлекаем все конфигурации, и каждый раз, когда мы добавляем сканер, нам нужно добавить только файл конфигурации JSON. После этого нам нужно только поддерживать эти файлы конфигурации. Если вы хотите более удобного управления, вы можете сохранить правила в базу данных, а затем подключиться к странице визуального управления.
Этот ресурс был впервые опубликован в личном блоге Цуй Цинцай Цзин Ми:Практическое руководство по разработке веб-краулера на Python3 | Цзин Ми
Если вы хотите узнать больше информации о поисковых роботах, обратите внимание на мой личный публичный аккаунт WeChat: Coder of Attack.
WeChat.QQ.com/Day/5 Это радость VE Z…(автоматическое распознавание QR-кода)