селен зі скрапом для динамічної сторінки


85

Я намагаюся зішкребти інформацію про товар із веб-сторінки за допомогою скрапінгу. Моя веб-сторінка, яку потрібно очистити, виглядає так:

  • починається зі сторінки списку товарів із 10 товарами
  • натискання кнопки "Далі" завантажує наступні 10 продуктів (URL не змінюється між двома сторінками)
  • я використовую LinkExtractor, щоб перейти за кожним посиланням на товар на сторінці товару та отримати всю необхідну мені інформацію

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

Мій павук досить стандартний, наприклад, такий:

class ProductSpider(CrawlSpider):
    name = "product_spider"
    allowed_domains = ['example.com']
    start_urls = ['http://example.com/shanghai']
    rules = [
        Rule(SgmlLinkExtractor(restrict_xpaths='//div[@id="productList"]//dl[@class="t2"]//dt'), callback='parse_product'),
        ]

    def parse_product(self, response):
        self.log("parsing product %s" %response.url, level=INFO)
        hxs = HtmlXPathSelector(response)
        # actual data follows

Будь-яка ідея цінується. Дякую!


Відповіді:


123

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

Ось приклад, як ви можете стежити за розбиттям сторінок на ebay, використовуючи Scrapy+ Selenium:

import scrapy
from selenium import webdriver

class ProductSpider(scrapy.Spider):
    name = "product_spider"
    allowed_domains = ['ebay.com']
    start_urls = ['http://www.ebay.com/sch/i.html?_odkw=books&_osacat=0&_trksid=p2045573.m570.l1313.TR0.TRC0.Xpython&_nkw=python&_sacat=0&_from=R40']

    def __init__(self):
        self.driver = webdriver.Firefox()

    def parse(self, response):
        self.driver.get(response.url)

        while True:
            next = self.driver.find_element_by_xpath('//td[@class="pagn-next"]/a')

            try:
                next.click()

                # get the data and write it to scrapy items
            except:
                break

        self.driver.close()

Ось кілька прикладів "павуків селену":


Існує також альтернатива тому, щоб використовувати Seleniumз Scrapy. У деяких випадках для обробки динамічних частин сторінки достатньо використання ScrapyJSпроміжного програмного забезпечення . Зразок реального використання:


спасибі за вашу допомогу. Насправді моя найбільша проблема в частині після next.click (). Щоразу, коли я отримую нову сторінку, але чи можу я все одно використовувати LinkExtractor для вилучення всіх URL-адрес продукту, а потім використовувати зворотний виклик для їх синтаксичного аналізу?
Z. Lin

2
Чи є спосіб повторно використати відповідь, яку вже схопив скрап, замість того, щоб використовувати self.driver.get(response.url)?
Ethereal

2
@HalcyonAbrahamRamirez це лише приклад із селеновою частиною у павука-скрепера. Після закінчення селену, як правило self.driver.page_source, передається екземпляру Selector для Scrapy, щоб проаналізувати HTML, сформувати екземпляри елементів, передати їх у конвеєри і т. Д. Або файли cookie селену можуть бути проаналізовані та передані Scrapy для надсилання додаткових запитів. Але якщо вам не потрібна потужність архітектури рамки скрапії, тоді, звичайно, ви можете використовувати просто селен - він сам досить потужний у пошуку елементів.
alecxe

4
@alecxe так, поки я розумію концепцію. Мене все ще бентежить частина, у якій ви витягуєте джерело сторінки за допомогою селену та передаєте елементи, які ви хочете скребти, на скрап. наприклад. Натиснувши кнопку "Завантажити більше", ви побачите більше елементів, але ви витягнете xpath для цих елементів. тепер як ви передаєте ці xpaths на скрапію? тому що лише елементи, показані при першому запиті на сторінку, будуть проаналізовані мізерним, а не ті, що натиснули кнопку завантажити більше із селеном
Halcyon Abraham Ramirez

2
@HalcyonAbrahamRamirez зрозумів, я завантажував би більше предметів, поки більше не було чого додати. Потім візьміть driver.page_sourceі передайте його на Selector()..
alecxe

2

Якщо (url не змінюється між двома сторінками), вам слід додати dont_filter = True до вашого scrap.Request () або scrapy знайде цю URL-адресу як копію після обробки першої сторінки.

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

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

Ось приклад:

class ScrollScraper(Spider):
    name = "scrollingscraper"

    quote_url = "http://quotes.toscrape.com/api/quotes?page="
    start_urls = [quote_url + "1"]

    def parse(self, response):
        quote_item = QuoteItem()
        print response.body
        data = json.loads(response.body)
        for item in data.get('quotes', []):
            quote_item["author"] = item.get('author', {}).get('name')
            quote_item['quote'] = item.get('text')
            quote_item['tags'] = item.get('tags')
            yield quote_item

        if data['has_next']:
            next_page = data['page'] + 1
            yield Request(self.quote_url + str(next_page))

Коли URL-адреса пагінації однакова для всіх сторінок і використовує запит POST, тоді ви можете використовувати scrapy.FormRequest () замість scrapy.Request () , обидва однакові, але FormRequest додає новий аргумент ( formdata = ) до конструктора.

Ось ще один приклад павука з цього допису :

class SpiderClass(scrapy.Spider):
    # spider name and all
    name = 'ajax'
    page_incr = 1
    start_urls = ['http://www.pcguia.pt/category/reviews/#paginated=1']
    pagination_url = 'http://www.pcguia.pt/wp-content/themes/flavor/functions/ajax.php'

    def parse(self, response):

        sel = Selector(response)

        if self.page_incr > 1:
            json_data = json.loads(response.body)
            sel = Selector(text=json_data.get('content', ''))

        # your code here

        # pagination code starts here
        if sel.xpath('//div[@class="panel-wrapper"]'):
            self.page_incr += 1
            formdata = {
                'sorter': 'recent',
                'location': 'main loop',
                'loop': 'main loop',
                'action': 'sort',
                'view': 'grid',
                'columns': '3',
                'paginated': str(self.page_incr),
                'currentquery[category_name]': 'reviews'
            }
            yield FormRequest(url=self.pagination_url, formdata=formdata, callback=self.parse)
        else:
            return
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.