Вопрос-Ответ

Can scrapy be used to scrape dynamic content from websites that are using AJAX?

Можно ли использовать scrapy для очистки динамического контента с веб-сайтов, использующих AJAX?

Недавно я изучал Python и запускаю руку в создание web-scraper. В нем нет ничего особенного; его единственная цель - получить данные с веб-сайта для ставок и перенести эти данные в Excel.

Большинство проблем решаемы, и у меня небольшая путаница. Однако я сталкиваюсь с огромным препятствием по поводу одной проблемы. Если сайт загружает таблицу лошадей и перечисляет текущие цены ставок, этой информации нет ни в одном исходном файле. Суть в том, что иногда эти данные являются актуальными, и цифры, очевидно, обновляются с какого-то удаленного сервера. В HTML на моем компьютере просто есть дыра, через которую их серверы пропускают все интересные данные, которые мне нужны.

Сейчас мой опыт работы с динамическим веб-контентом невелик, поэтому мне трудно разобраться в этом вопросе.

Я думаю, что Java или Javascript - это ключ, это часто всплывает.

Scraper - это просто механизм сравнения шансов. На некоторых сайтах есть API, но мне это нужно для тех, у кого их нет. Я использую библиотеку scrapy с Python 2.7

Приношу свои извинения, если этот вопрос слишком открытый. Короче говоря, мой вопрос таков: как можно использовать scrapy для очистки этих динамических данных, чтобы я мог их использовать? Чтобы я мог собирать данные о коэффициентах ставок в режиме реального времени?


Смотрите также: Как я могу очистить страницу с динамическим контентом (созданную JavaScript) в Python? для общего случая.

Переведено автоматически
Ответ 1

Вот простой пример scrapy с AJAX-запросом. Давайте посмотрим сайт rubin-kazan.ru.

Все сообщения загружаются с помощью AJAX-запроса. Моя цель - получить эти сообщения со всеми их атрибутами (автор, дата, ...):

введите описание изображения здесь

Когда я анализирую исходный код страницы, я не вижу всех этих сообщений, потому что веб-страница использует технологию AJAX. Но я могу с помощью Firebug из Mozilla Firefox (или аналогичного инструмента в других браузерах) проанализировать HTTP-запрос, который генерирует сообщения на веб-странице:

введите описание изображения здесь

Он перезагружает не всю страницу, а только те части страницы, которые содержат сообщения. Для этого я нажимаю произвольный номер страницы внизу:

enter image description here

And I observe the HTTP request that is responsible for message body:

enter image description here

After finish, I analyze the headers of the request (I must quote that this URL I'll extract from source page from var section, see the code below):

enter image description here

And the form data content of the request (the HTTP method is "Post"):

enter image description here

And the content of response, which is a JSON file:

введите описание изображения здесь

В котором представлена вся информация, которую я ищу.

С этого момента я должен реализовать все эти знания в scrapy. Давайте определим spider для этой цели:

class spider(BaseSpider):
name = 'RubiGuesst'
start_urls = ['http://www.rubin-kazan.ru/guestbook.html']

def parse(self, response):
url_list_gb_messages = re.search(r'url_list_gb_messages="(.*)"', response.body).group(1)
yield FormRequest('http://www.rubin-kazan.ru' + url_list_gb_messages, callback=self.RubiGuessItem,
formdata={'page': str(page + 1), 'uid': ''})

def RubiGuessItem(self, response):
json_file = response.body

В parse функции у меня есть ответ на первый запрос.
В RubiGuessItem у меня есть файл JSON со всей информацией.

Ответ 2

Браузеры на базе Webkit (например, Google Chrome или Safari) имеют встроенные инструменты разработчика. В Chrome вы можете открыть егоMenu->Tools->Developer Tools. На Network вкладке можно просмотреть всю информацию о каждом запросе и ответе:

введите описание изображения здесь

Внизу рисунка вы можете видеть, что я отфильтровал запрос до XHR - это запросы, выполняемые кодом javascript.

Совет: журнал очищается каждый раз, когда вы загружаете страницу, кнопка с черной точкой внизу рисунка сохраняет журнал.

После анализа запросов и ответов вы можете имитировать эти запросы от вашего веб-сканера и извлекать ценные данные. Во многих случаях получить ваши данные будет проще, чем парсить HTML, потому что эти данные не содержат логики представления и отформатированы для доступа с помощью кода javascript.

В Firefox есть аналогичное расширение, оно называется firebug. Некоторые возразят, что firebug еще мощнее, но мне нравится простота webkit.

Ответ 3

Часто при сканировании мы сталкиваемся с проблемами, когда контент, отображаемый на странице, генерируется с помощью Javascript, и поэтому scrapy не может выполнить сканирование для него (например, запросы ajax, сумасшествие jQuery).

Однако, если вы используете Scrapy вместе с платформой веб-тестирования Selenium, мы сможем сканировать все, что отображается в обычном веб-браузере.

Некоторые моменты, на которые следует обратить внимание:


  • Для этого у вас должна быть установлена версия Selenium RC на Python, и вы должны правильно настроить Selenium. Также это всего лишь поисковик шаблонов. Вы могли бы сделать гораздо более безумные и продвинутые вещи, но я просто хотел показать основную идею. В нынешнем виде кода вы будете выполнять два запроса для любого заданного URL. Один запрос выполняется Scrapy, а другой - Selenium. Я уверен, что есть способы обойти это, так что вы могли бы просто заставить Selenium выполнять один-единственный запрос, но я не потрудился реализовать это, и, выполнив два запроса, вы также сможете сканировать страницу с помощью Scrapy.


  • Это довольно мощное решение, потому что теперь вам доступен весь отрисованный DOM для обхода, и вы по-прежнему можете использовать все полезные функции обхода в Scrapy. Конечно, это приведет к более медленному сканированию, но в зависимости от того, насколько вам нужен отрисованный DOM, возможно, стоит подождать.


    from scrapy.contrib.spiders import CrawlSpider, Rule
    from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
    from scrapy.selector import HtmlXPathSelector
    from scrapy.http import Request

    from selenium import selenium

    class SeleniumSpider(CrawlSpider):
    name = "SeleniumSpider"
    start_urls = ["http://www.domain.com"]

    rules = (
    Rule(SgmlLinkExtractor(allow=('\.html', )), callback='parse_page',follow=True),
    )

    def __init__(self):
    CrawlSpider.__init__(self)
    self.verificationErrors = []
    self.selenium = selenium("localhost", 4444, "*chrome", "http://www.domain.com")
    self.selenium.start()

    def __del__(self):
    self.selenium.stop()
    print self.verificationErrors
    CrawlSpider.__del__(self)

    def parse_page(self, response):
    item = Item()

    hxs = HtmlXPathSelector(response)
    #Do some XPath selection with Scrapy
    hxs.select('//div').extract()

    sel = self.selenium
    sel.open(response.url)

    #Wait for javscript to load in Selenium
    time.sleep(2.5)

    #Do some crawling of javascript created content with Selenium
    sel.get_text("//div")
    yield item

    # Snippet imported from snippets.scrapy.org (which no longer works)
    # author: wynbennett
    # date : Jun 21, 2011

Ссылка: http://snipplr.com/view/66998 /

Ответ 4

Другим решением было бы реализовать обработчик загрузки или промежуточное программное обеспечение для обработки загрузки. (см. Документы scrapy для получения дополнительной информации о промежуточном программном обеспечении для загрузчика) Ниже приведен пример класса, использующего selenium с безголовым phantomjs webdriver:

1) Определите класс в middlewares.py скрипте.

from selenium import webdriver
from scrapy.http import HtmlResponse

class JsDownload(object):

@check_spider_middleware
def process_request(self, request, spider):
driver = webdriver.PhantomJS(executable_path='D:\phantomjs.exe')
driver.get(request.url)
return HtmlResponse(request.url, encoding='utf-8', body=driver.page_source.encode('utf-8'))

2) Добавить JsDownload() класс к переменной DOWNLOADER_MIDDLEWARE внутри settings.py:

DOWNLOADER_MIDDLEWARES = {'MyProj.middleware.MiddleWareModule.MiddleWareClass': 500}

3) Интегрируйте HTMLResponse внутри your_spider.py. Расшифровав тело ответа, вы получите желаемый результат.

class Spider(CrawlSpider):
# define unique name of spider
name = "spider"

start_urls = ["https://www.url.de"]

def parse(self, response):
# initialize items
item = CrawlerItem()

# store data as items
item["js_enabled"] = response.body.decode("utf-8")

Optional Addon:

I wanted the ability to tell different spiders which middleware to use so I implemented this wrapper:

def check_spider_middleware(method):
@functools.wraps(method)
def wrapper(self, request, spider):
msg = '%%s %s middleware step' % (self.__class__.__name__,)
if self.__class__ in spider.middleware:
spider.log(msg % 'executing', level=log.DEBUG)
return method(self, request, spider)
else:
spider.log(msg % 'skipping', level=log.DEBUG)
return None

return wrapper

for wrapper to work all spiders must have at minimum:

middleware = set([])

to include a middleware:

middleware = set([MyProj.middleware.ModuleName.ClassName])

Преимущество:
Основное преимущество реализации этого способа, а не в spider, заключается в том, что в конечном итоге вы делаете только один запрос. Например, в решении A T: обработчик загрузки обрабатывает запрос, а затем передает ответ spider. Затем spider создает совершенно новый запрос в своей функции parse_page - это два запроса к одному и тому же контенту.

python