Очистка веб-сайтов с помощью Python3 (Scrapy, BS4) дает неполные данные. Не могу понять, почему

#python-3.x #web-scraping #scrapy

#python-3.x #веб-очистка #scrapy

Вопрос:

Некоторое время назад я настроил веб-скребок с использованием BS4, регистрируя стоимость виски каждый день

 import requests
from bs4 import BeautifulSoup    

def getPrice() -> float:
    try:
        URL = "https://www.thewhiskyexchange.com/p/2940/suntory-yamazaki-12-year-old"
        website = requests.get(URL)
    except:
        print("ERROR requesting Price")
    
    try:
        soup = BeautifulSoup(website.content, 'html.parser')
        price = str(soup.find("p", class_="product-action__price").next)
        price = float(price[1::])
        return price
    except:
        print("ERROR parsing Price")
  

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

На этот раз я пытался очистить другие сайты от данных о других виски, используя SCRAPY.

Я попробовал следующие URL-адреса:

https://www.thegrandwhiskyauction.com/past-auctions/q-macallan/180-per-page/relevance

https://www.ebay.de/sch/i.html?_sacat=0amp;LH_Complete=1amp;_udlo=amp;_udhi=amp;_samilow=amp;_samihi=amp;_sadis=10amp;_fpos=amp;LH_SALE_CURRENCY=0amp;_sop=12amp;_dmd=1amp;_fosrp=1amp;_nkw=macallanamp;rt=nc

 import scrapy


class QuotesSpider(scrapy.Spider):
    name = "whisky"

    def start_requests(self):
        user_agent = 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36'
        urls = [
            'https://www.thegrandwhiskyauction.com/past-auctions/q-macallan/180-per-page/relevance',
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        page = response.url.split("/")[-2]
        filename = f'whisky-{page}.html'
        #data = response.css('.itemDetails').getall()
        with open(filename, 'wb') as f:
                f.write(response.body)
  

Я просто настроил базовый пример из руководства для создания быстрого прототипа выше.
Однако он не вернул полный веб-сайт. В теле ответа действительно отсутствовали несколько тегов и особенно контент, который я искал.

Я снова попытался решить это с помощью BS4 следующим образом:

 import requests
from bs4 import BeautifulSoup

URL = "https://www.thegrandwhiskyauction.com/past-auctions/q-macallan/180-per-page/relevance"
website = requests.get(URL)
soup = BeautifulSoup(website.content, 'html.parser')

with open("whiskeySoup.html", 'w') as f:
    f.write(str(soup.body))
  

К моему удивлению, это привело к тому же результату. Запрос и его тело не содержали полного веб-сайта, отсутствовали все данные, которые я искал.

Я также включил заголовок user-agent, поскольку узнал, что некоторые сайты распознают запросы от ботов и пауков и не доставляют все свои данные. Однако это не решило проблему.

Я не могу выяснить или отладить, почему данные, запрошенные с этих URL-адресов, являются неполными. Есть ли способ решить эту проблему с помощью SCRAPY?

Ответ №1:

Многие веб-сайты в значительной степени полагаются на javascript для создания окончательной HTML-страницы веб-сайта. Когда вы отправляете запрос на сервер, он возвращает html-код с помощью некоторых скриптовых веб-браузеров, таких как Chrome, Firefox и другие, обрабатывают этот код javascript, и появляется конечный html, который вы можете видеть. Но когда вы используете scrapy, request или какую-либо библиотеку, они не имеют функциональности выполнения кода javascript, и, следовательно, html-код отличается от html, и поскольку поисковый робот видит веб-страницу. Если вы хотите увидеть, как поисковый робот видит веб-сайт (html-код веб-страницы, видимый сканером), вы можете запустить команду ‘scrapy view {url}’, это откроет страницу в браузере, или если вы хотите получить html-код веб-страницы, видимый сканером, вы можете запустить команду ‘scrapy fetch{url}’. Когда вы работаете с scrapy, рекомендуется открыть URL-адрес в оболочке (команда ‘scrapy shell {url}’ ), а затем протестировать вашу логику извлечения желаемого содержимого с помощью метода xpath или css (response.css (‘some_css’).css (‘again_some_css’). )и затем, наконец, добавьте эту логику в свой окончательный поисковый робот. Если вы хотите посмотреть, какой ответ вы получили в shell. вы можете просто ввести view (ответ), и он откроет ответ, полученный в браузере. Надеюсь, это понятно. Но если вы хотите обработать javascript перед окончательной обработкой ответа (когда это необходимо), вы можете использовать selenium, который является безголовым браузером, или splash, который является легким веб-браузером. selenium довольно прост в использовании.

Редактировать 1. Для первого URL: перейдите в оболочку scrapy и проверьте путь css div.bidPrice::text. Внутри этого вы увидите, что содержимое внутри генерируется динамически, и нет html-кода, а содержимое генерируется динамически.

Комментарии:

1. Спасибо, но я все еще немного в замешательстве. Пытаясь использовать первый упомянутый мной URL-адрес, scrapy view {url} и scrapy shell {url} view (response) отображают веб-сайт правильно, то есть заполняют все данные, которые я ищу. Цена корректно доступна через css path div.bidPrice::text в предварительном просмотре ответа в браузере. Однако response.css(«div.bidPrice::text»).getall() возвращает только некоторое «n».

2. в оболочке scrapy запустите response.css(‘div.bidPrice’).get() здесь вы увидите первый блок div, поскольку он извлекается сканером. здесь цена предложения и другой текст внутри поля отсутствуют, вместо этого есть некоторые переменные, такие как%bid_descriptor_text% %multi_currency% и тому подобное. Но когда тот же HTML-код просматривается в веб-браузере, я думаю, должен быть какой-скрипт в код HTML код ( добавлено с <script> tag ) which gets executed when this code is opened with browser. но при запуске из shell n внутри этого div присутствует только текст. Я думаю, что здесь необходим selenium.