Чи можна скрапію використовувати для вискоблювання динамічного вмісту з веб-сайтів, які використовують AJAX?


145

Я нещодавно вивчаю Python і занурюю руку в створення веб-скрепера. Це взагалі нічого фантазії; Єдина його мета - вилучення даних із веб-сайтів, що надають ставки, і передача цих даних в Excel.

Більшість питань вирішуються, і в мене непогано стоїть проблема. Однак я зачіпаю масштабну перешкоду через одне питання. Якщо сайт завантажує таблицю коней і перераховує поточні ціни ставок, ця інформація відсутня у жодному вихідному файлі. Поняття полягає в тому, що ці дані іноді живуть, очевидно, що номери оновлюються з певного віддаленого сервера. HTML на моєму ПК просто має дірку, куди їх сервери просувають усі цікаві дані, які мені потрібні.

Зараз мій досвід динамічного веб-контенту низький, тому ця річ - це те, що я маю проблеми з головою.

Я думаю, що Java або Javascript - це ключ, це часто з’являється.

Скрепер - це просто механізм порівняння шансів. Деякі сайти мають API, але мені це потрібно для тих, хто цього не має. Я використовую бібліотеку скрапінгу з Python 2.7

Я прошу вибачення, якщо це питання є занадто відкритим. Якщо коротко, моє запитання: як скрапію можна використовувати для скребкування цих динамічних даних, щоб я міг їх використовувати? Так що я можу викреслити дані шансів на ставку в режимі реального часу?


1
Як я можу отримати ці дані, динамічні та живі дані?
Йосип

1
Якщо на вашій сторінці є JavaScript, спробуйте це
reclosedev

3
Спробуйте на деяких Firefoxрозширень , наприклад , httpFoxабо liveHttpHeadersі завантажити сторінку , яка використовує AJAX запит. Scrap не автоматично визначає запити ajax, вам доведеться вручну шукати відповідну URL-адресу ajax, а потім робити запит із цим.
Аамір Аднан

привіт, я дам розширення Firefox wizz
Джозеф

Існує ряд рішень з відкритим кодом. Але якщо ви шукаєте простий і швидкий спосіб зробити це особливо для великих навантажень, перегляньте SnapSearch ( snapsearch.io ). Він був розроблений для сайтів JS, HTML5 та SPA, які потребують сканування. Спробуйте демонстрацію (якщо там порожній вміст, це означає, що сайт фактично не повернув вміст тіла, потенційно означає переспрямування 301).
CMCDragonkai

Відповіді:


74

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

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

У нижній частині малюнка ви бачите, що я відфільтрував запит до XHR- це запити, зроблені кодом JavaScript.

Порада: журнал очищається щоразу, коли ви завантажуєте сторінку, внизу зображення кнопка чорної крапки зберігатиме журнал.

Проаналізувавши запити та відповіді, ви зможете імітувати ці запити у веб-сканера та витягувати цінні дані. У багатьох випадках отримати прості дані буде простіше, ніж розбирати HTML, оскільки ці дані не містять логіки презентації та відформатовані для доступу до коду javascript.

Firefox має подібне розширення, його називають firebug . Дехто стверджує, що firebug є ще більш потужним, але мені подобається простота веб-файлів.


141
Як, скаже, це може бути прийнятою відповіддю, якщо в ній навіть немає слова «скрап» ??
Інструментарій

Це працює, і його легко проаналізувати за допомогою модуля json в python. Це рішення! У порівнянні з цим, спробуйте використовувати селен або інші речі, які люди пропонують, це більше болить голова. Якщо альтернативний метод був би більш заплутаним, то я б надіслав його вам, але це не так тут @Toolkit
Arion_Miles

1
Це насправді не актуально. Питання полягало в тому, як використовувати нечіткі для того, щоб викреслити динамічні веб-сайти.
Е. Ерфан

"Як чорт може це прийняти відповідь" - Тому що практичне використання перемагає політичну коректність. Люди розуміють КОНТЕКСТ.
Еспресо

98

Ось простий приклад scrapyзапиту AJAX. Давайте подивіться на сайті rubin-kazan.ru .

Усі повідомлення завантажуються із запитом AJAX. Моя мета - отримати ці повідомлення з усіма їх атрибутами (автором, датою, ...):

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

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

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

Він не завантажує всю сторінку, а лише частини сторінки, що містять повідомлення. Для цього натискаю довільну кількість сторінки внизу:

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

І я спостерігаю за запитом HTTP, який відповідає за тіло повідомлення:

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

Після закінчення аналізую заголовки запиту (мушу цитувати, що цю URL-адресу я витягну зі сторінки джерела з розділу var, дивіться код нижче):

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

І вміст даних форми запиту (метод HTTP - "Опублікувати"):

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

І вміст відповіді, який є файлом JSON:

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

У якому представлена ​​вся інформація, яку я шукаю.

Відтепер я повинен впроваджувати всі ці знання в скрапію. Давайте для цього визначимо павука:

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 з усією інформацією.


6
Привіт. Чи можете ви поясніть, що таке "url_list_gb_messages"? Я не можу це зрозуміти. Дякую.
поляризуйте

4
Це, безумовно, краще.
1a1a11a

1
@polarise Цей код використовує reмодуль (регулярні вирази), він шукає рядок 'url_list_gb_messages="(.*)"'і ізолює вміст дужок у однойменній змінній. Це приємне вступ: guru99.com/python-regular-expressions-complete-tutorial.html
MGP

42

Багато разів під час сканування ми стикаємося з проблемами, коли вміст, який відображається на сторінці, генерується за допомогою Javascript, і тому scrap не може переповнювати його (наприклад, запити ajax, божевілля jQuery).

Однак якщо ви використовуєте Scrapy разом із рамкою веб-тестування Selenium, ми можемо сканувати все, що відображається у звичайному веб-браузері.

Деякі речі, які слід зазначити:

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

  • Це досить потужно, тому що тепер у вас є весь доступний DOM для сканування, і ви все ще можете використовувати всі приємні функції сканування в Scrap. Це дозволить повільніше сканувати звичайно, але залежно від того, наскільки вам потрібен наданий 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/


Охайне рішення! Чи є якісь поради щодо підключення цього сценарію до Firefox? (ОС - Linux Mint). Я отримую "[Errno 111] З'єднання відмовлено".
Андрій

1
Цей код більше не працює selenium=3.3.1і python=2.7.10помилка при імпорті селену з селену
Бенджамінц

1
У цій версії селену вашим заявою про імпорт буде: from selenium import webdriverабо chromedriverабо що б ви не використовували. Документи EDIT: Додайте посилання на документацію та змініть мою жахливу граматику!
nulltron

Віддалене керування Selenium було замінено на Selenium WebDriver, згідно з їх веб-сайтом
rainbowsorbet

33

Іншим рішенням може бути реалізація обробника завантаження або завантаження проміжного програмного забезпечення. (див. Документи scrap для отримання додаткової інформації про завантажувальне програмне забезпечення) Далі наведено приклад класу, що використовує селен із безголовним веб-драйвером phantomjs:

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") 

Необов’язковий Addon:
Я хотів можливість сказати різним павукам, які проміжні програми використовувати, щоб я реалізував цю обгортку:

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

щоб обгортка працювала, всі павуки повинні мати як мінімум:

middleware = set([])

включити проміжне програмне забезпечення:

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

Перевага:
Основна перевага в реалізації цього способу, а не в павуці, полягає в тому, що ви в остаточному підсумку робите лише один запит. Наприклад, у рішенні AT: Обробник завантаження обробляє запит, а потім передає відповідь павуку. Потім павук робить абсолютно новий запит у своїй функції parse_page - це два запити на один і той же вміст.


Я зовсім трохи спізнився відповісти на це, хоча>. <
rocktheartsm4l

@ Rocktheartsm4l , що трапилося тільки з використанням, в process_requests, if spider.name in ['spider1', 'spider2']замість декоратора
майданчики

@pad У цьому немає нічого поганого. Щойно я зрозумів, що для моїх занять павуком є ​​набір з назвою проміжне програмне забезпечення. Таким чином я міг подивитися на будь-який клас павуків і точно побачити, які середні вироби будуть виконані за нього. У моєму проекті було реалізовано багато проміжних програм, тому це мало сенс.
rocktheartsm4l

Це жахливе рішення. Мало того, що це не пов’язано зі скрапією, але й сам код є надзвичайно неефективним, а також весь підхід загалом перемагає цілі асинхронної рамки скребки, що є скрапією
Granitosaurus

2
Його набагато ефективніше, ніж будь-яке інше рішення, яке я бачив у програмі SO, оскільки використання середнього посуду для завантажувача робить це таким чином, що для сторінки робиться лише один запит. Якщо це так страшно, чому ви не придумаєте кращого рішення та поділитесь замість цього висловлюючи відверто однобічні претензії. "Не пов'язані зі скрапією", ви курите щось? Окрім впровадження якогось божевільного складного, надійного та спеціального рішення, це такий підхід, який я бачив у більшості людей. Різниця полягає лише в тому, що більшість реалізує селенову частину в павуку, що викликає багато запитів ...
rocktheartsm4l

10

Я використовував проміжне програмне забезпечення для користувальницького завантажувача, але не дуже задоволений ним, оскільки мені не вдалося змусити кеш працювати з ним.

Кращим підходом було впровадження користувальницького обробника завантажень.

Існує робочий приклад тут . Це виглядає приблизно так:

# encoding: utf-8
from __future__ import unicode_literals

from scrapy import signals
from scrapy.signalmanager import SignalManager
from scrapy.responsetypes import responsetypes
from scrapy.xlib.pydispatch import dispatcher
from selenium import webdriver
from six.moves import queue
from twisted.internet import defer, threads
from twisted.python.failure import Failure


class PhantomJSDownloadHandler(object):

    def __init__(self, settings):
        self.options = settings.get('PHANTOMJS_OPTIONS', {})

        max_run = settings.get('PHANTOMJS_MAXRUN', 10)
        self.sem = defer.DeferredSemaphore(max_run)
        self.queue = queue.LifoQueue(max_run)

        SignalManager(dispatcher.Any).connect(self._close, signal=signals.spider_closed)

    def download_request(self, request, spider):
        """use semaphore to guard a phantomjs pool"""
        return self.sem.run(self._wait_request, request, spider)

    def _wait_request(self, request, spider):
        try:
            driver = self.queue.get_nowait()
        except queue.Empty:
            driver = webdriver.PhantomJS(**self.options)

        driver.get(request.url)
        # ghostdriver won't response when switch window until page is loaded
        dfd = threads.deferToThread(lambda: driver.switch_to.window(driver.current_window_handle))
        dfd.addCallback(self._response, driver, spider)
        return dfd

    def _response(self, _, driver, spider):
        body = driver.execute_script("return document.documentElement.innerHTML")
        if body.startswith("<head></head>"):  # cannot access response header in Selenium
            body = driver.execute_script("return document.documentElement.textContent")
        url = driver.current_url
        respcls = responsetypes.from_args(url=url, body=body[:100].encode('utf8'))
        resp = respcls(url=url, body=body, encoding="utf-8")

        response_failed = getattr(spider, "response_failed", None)
        if response_failed and callable(response_failed) and response_failed(resp, driver):
            driver.close()
            return defer.fail(Failure())
        else:
            self.queue.put(driver)
            return defer.succeed(resp)

    def _close(self):
        while not self.queue.empty():
            driver = self.queue.get_nowait()
            driver.close()

Припустимо, ваш скребок називається "скребок". Якщо ви помістите згаданий код всередині файлу, який називається handlers.py, у корені папки "скрепер", то ви можете додати його до settings.py:

DOWNLOAD_HANDLERS = {
    'http': 'scraper.handlers.PhantomJSDownloadHandler',
    'https': 'scraper.handlers.PhantomJSDownloadHandler',
}

І voilà, JS розібрав DOM, з кеш-скрипінгом, повторами тощо.


Мені подобається це рішення!
rocktheartsm4l

Приємне рішення. Чи є драйвер Selenium єдиним варіантом?
Мотей

Прекрасне рішення. Дуже дякую.
CrazyGeek

4

як скрапію можна використовувати для викреслення цих динамічних даних, щоб я міг їх використовувати?

Цікаво, чому ніхто не опублікував рішення, використовуючи лише Scrapy.

Перегляньте допис в блозі від команди ScrapPING INFINITE SCROLLING PAGES . Приклад записів http://spidyquotes.herokuapp.com/scroll веб-сайт, який використовує нескінченну прокрутку.

Ідея полягає у використанні Інструментів для розробників у вашому браузері та помічанні запитів AJAX, а потім на основі цієї інформації створюйте запити на Scrap .

import json
import scrapy


class SpidyQuotesSpider(scrapy.Spider):
    name = 'spidyquotes'
    quotes_base_url = 'http://spidyquotes.herokuapp.com/api/quotes?page=%s'
    start_urls = [quotes_base_url % 1]
    download_delay = 1.5

    def parse(self, response):
        data = json.loads(response.body)
        for item in data.get('quotes', []):
            yield {
                'text': item.get('text'),
                'author': item.get('author', {}).get('name'),
                'tags': item.get('tags'),
            }
        if data['has_next']:
            next_page = data['page'] + 1
            yield scrapy.Request(self.quotes_base_url % next_page)

Ми знову стикаємося з тією ж проблемою: Scrappy не робиться для цієї мети, і саме тут ми стикаємося з тією ж проблемою. Перейдіть до phantomJS або як запропонували інші, створіть власне
середнє

@ rak007 PhantomJS vs драйвер Chrome. Який би ви запропонували?
Чанкі Патхак

2

так, Scrap може видаляти динамічні веб-сайти, веб-сайт, що надається через javaScript.

Існує два підходи до розбору таких веб-сайтів.

Перший,

ви можете використовувати splashдля візуалізації коду Javascript, а потім проаналізувати відтворений HTML. ви можете знайти документа та проект тут Scrap splash, git

По-друге,

Як всі заявляють, спостерігаючи за network callsтак, ви можете знайти дзвінок api, який отримує дані, і глузувати з цього дзвінка у вашому павуці-скрапі, може допомогти вам отримати потрібні дані.


1

Я обробляю запит ajax за допомогою Selenium та веб-драйвера Firefox. Це не так швидко, якщо вам потрібен сканер як демон, але набагато краще, ніж будь-яке рішення вручну. Я написав короткий підручник тут для довідки

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.