Python Requests (Web Scraping) - создание файла cookie для просмотра данных на веб-сайте. - PullRequest
0 голосов
/ 26 августа 2018

Я пытаюсь очистить финансовый веб-сайт, чтобы создать приложение, которое сравнивает точность финансовых данных с других веб-сайтов (google / yahoo finance).Это личный проект, который я начал на самом деле просто для изучения программирования на Python и написания скриптов.

URL-адрес, который я пытаюсь проанализировать (в частности, «Ключевые данные», такие как Market Cap, Volume, Etc), находится здесь:

https://www.marketwatch.com/investing/stock/sbux

Я понял (с помощью других), что файл cookie должен создаваться и отправляться с каждым запросом, чтобы страница отображала данные (в противном случаеответ html-страницы почти пусто).

Я использовал браузеры Opera / Firefox / Chrome для просмотра заголовков HTTP и запросов, которые отправляются обратно из браузера.Я пришел к выводу, что необходимо выполнить 3 шага / запроса, чтобы получить все данные cookie и построить их по частям.

Шаг / запрос 1

Просто зайдите на указанный выше URL.

GET /investing/stock/sbux HTTP/1.1
Host: www.marketwatch.com:443
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 OPR/55.0.2994.44

HTTP/1.1 200 OK
Cache-Control: max-age=0, no-cache, no-store
Connection: keep-alive
Content-Length: 579
Content-Type: text/html; charset=utf-8
Date: Sun, 26 Aug 2018 05:12:16 GMT
Expires: Sun, 26 Aug 2018 05:12:16 GMT
Pragma: no-cache

Шаг / запрос 2

Я не уверен, откуда взялся этот URL-адрес POST.Однако при использовании Firefox и просмотре сетевых подключений этот URL-адрес появился во вкладке «Trace Stack».Опять же, я понятия не имею, где взять этот URL, если он одинаков для всех или создан случайно.Я также не знаю, какие данные POST отправляются или откуда взяты значения X-Hash-Result или X-Token-Value.Тем не менее, этот запрос возвращает очень важное значение в заголовке ответа со следующей строкой: 'Set-Cookie: ncg_g_id_zeta = 701c19ee3f45d07b56b40fb8e313214d' эта часть cookie имеет решающее значение для следующего запроса, чтобы вернуть полныйcookie и получить данные на веб-странице.

POST /149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/fingerprint HTTP/1.1
Host: www.marketwatch.com:443
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Content-Type: application/json; charset=UTF-8
Origin: https://www.marketwatch.com
Referer: https://www.marketwatch.com/investing/stock/sbux
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 OPR/55.0.2994.44
X-Hash-Result: 701c19ee3f45d07b56b40fb8e313214d
X-Token-Value: 900c4055-ef7a-74a8-e9ec-f78f7edc363b

HTTP/1.1 200 OK
Cache-Control: max-age=0, no-cache, no-store
Connection: keep-alive
Content-Length: 17
Content-Type: application/json; charset=utf-8
Date: Sun, 26 Aug 2018 05:12:16 GMT
Expires: Sun, 26 Aug 2018 05:12:16 GMT
Pragma: no-cache
Set-Cookie: ncg_g_id_zeta=701c19ee3f45d07b56b40fb8e313214d; Path=/; HttpOnly

Шаг / запрос 3

Этот запрос отправляется на исходный URL с файлом cookie, выбранным вшаг 2. Затем в ответе возвращается полный файл cookie, который можно использовать на шаге 1, чтобы избежать повторения шагов 2 и 3.Также будет отображена полная страница данных.

GET /investing/stock/sbux HTTP/1.1
Host: www.marketwatch.com:443
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: ncg_g_id_zeta=701c19ee3f45d07b56b40fb8e313214d
Referer: https://www.marketwatch.com/investing/stock/sbux
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 OPR/55.0.2994.44

HTTP/1.1 200 OK
Cache-Control: max-age=0, no-cache, no-store
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 62944
Content-Type: text/html; charset=utf-8
Date: Sun, 26 Aug 2018 05:12:17 GMT
Expires: Sun, 26 Aug 2018 05:12:17 GMT
Pragma: no-cache
Server: Kestrel
Set-Cookie: seenads=0; expires=Sun, 26 Aug 2018 23:59:59 GMT; domain=.marketwatch.com; path=/
Set-Cookie: mw_loc=%7B%22country%22%3A%22CA%22%2C%22region%22%3A%22ON%22%2C%22city%22%3A%22MARKHAM%22%2C%22county%22%3A%5B%22%22%5D%2C%22continent%22%3A%22NA%22%7D; expires=Sat, 01 Sep 2018 23:59:59 GMT; domain=.marketwatch.com; path=/
Vary: Accept-Encoding
x-frame-options: SAMEORIGIN
x-machine: 8cfa9f20bf3eb

Сводка

В итоге, шаг 2 является наиболее важным для получения оставшейся части cookie ...Но я не могу понять 3 вещи:

1) Откуда берется POST-URL (не встроенный в исходную страницу, является ли URL-адрес одинаковым для всех или он генерируется случайным образом)по сайту).

2) Какие данные отправляются в запросе POST?

3) Где X-Hash-Resultа X-Token-Value откуда?Нужно ли отправлять его в заголовке вместе с запросом?

Это было для меня хорошим испытанием, на которое я потратил несколько часов (я также очень плохо знаком с Python и HTTP Web Requests)и поэтому я чувствую, что кто-то с большим опытом может решить эту проблему более своевременно.

Спасибо всем за всех, кто может помочь.

1 Ответ

0 голосов
/ 28 августа 2018

Привет снова FromThe6ix!

Сегодня вечером я провел некоторое время, пытаясь заставить строку cookie начать работу. MarketWatch проделал довольно приличную работу по защите своих данных. Чтобы создать весь файл cookie, вам понадобится ключ API wsj (я думаю, поставщик финансовых данных их сайта) и некоторые скрытые переменные, которые потенциально доступны только для сервера клиента и строго скрыты в зависимости от вашего веб-драйвера или его отсутствия. его.

Например, если вы пытаетесь выполнить запрос: POST https://browser.pipe.aria.microsoft.com/Collector/3.0/?qsp=true&content-type=application/bond-compact-binary&client-id=NO_AUTH&sdk-version=ACT-Web-JS-2.7.1&x-apikey=c34cce5c21da4a91907bc59bce4784fb-42e261e9-5073-49df-a2e1-42415e012bc6-6954

Вы получите 400 несанкционированных ошибок.

Помните, что существует также большая вероятность того, что мастер кластера клиентского хост-сервера и различные API-интерфейсы, с которыми он взаимодействует, обмениваются данными без того, чтобы наши браузеры могли получать сетевой трафик. Это можно сделать через какое-то промежуточное программное обеспечение, например. Я полагаю, что может объяснить недостающие значения X-Hash-Result и X-Token-Value.

Я не говорю, что невозможно создать эту строку cookie, просто это неэффективный путь с точки зрения времени и усилий на разработку. Теперь я также подвергаю сомнению легкость масштабируемости этого метода с точки зрения использования различных тикеров, кроме AAPL. Если нет явного требования не использовать веб-драйвер и / или сценарий не должен быть легко переносимым без какой-либо конфигурации, разрешенной вне pip install, я бы не выбрал этот метод.

Это, в сущности, оставляет нас либо с Scrapy Spider, либо с Selenium Scraper (и, к сожалению, с небольшой дополнительной конфигурацией среды, но очень важными навыками, которые нужно выучить, если вы хотите писать и развертывать веб-скребки. запросы + bs4 - для идеальных простых операций очистки / необычной переносимости кода).

Я разработал Selenium Scraper ETL Class, используя для вас веб-драйвер PhantomJS. Он принимает строку тикера в качестве параметра и работает с другими акциями, кроме AAPL. Это было непросто, так как marketwatch.com не будет перенаправлять трафик с веб-драйвера PhantomJS (я могу сказать, что они потратили много ресурсов, пытаясь отговорить веб-скребков. Кстати, гораздо больше, чем, скажем, yahoo.com).

В любом случае, вот последний сценарий Selenium, он работает на питоне 2 и 3:

# Market Watch Test Scraper ETL
# Tested on python 2.7 and 3.5
# IMPORTANT: Ensure PhantomJS Web Driver is configured and installed

import pip
import sys
import signal
import time


# Package installer function to handle missing packages
def install(package):
    print(package + ' package for Python not found, pip installing now....')
    pip.main(['install', package])
    print(package + ' package has been successfully installed for Python\n Continuing Process...')

# Ensure beautifulsoup4 is installed
try:
    from bs4 import BeautifulSoup
except:
    install('beautifulsoup4')
    from bs4 import BeautifulSoup

# Ensure selenium is installed
try:
    from selenium import webdriver
    from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
except:
    install('selenium')
    from selenium import webdriver
    from selenium.webdriver.common.desired_capabilities import DesiredCapabilities


# Class to extract and transform raw marketwatch.com financial data
class MarketWatchETL:

    def __init__(self, ticker):
        self.ticker = ticker.upper()
        # Set up desired capabilities to spoof Firefox since marketwatch.com rejects any PhantomJS Request
        self._dcap = dict(DesiredCapabilities.PHANTOMJS)
        self._dcap["phantomjs.page.settings.userAgent"] = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) "
                                                           "AppleWebKit/537.36 (KHTML, like Gecko) "
                                                           "Chrome/29.0.1547.57 Safari/537.36")
        self._base_url = 'https://www.marketwatch.com/investing/stock/'
        self._retries = 10

    # Private Static Method to clean and organize Key Data Extract
    @staticmethod
    def _cleaned_key_data_object(raw_data):
        cleaned_data = {}
        raw_labels = raw_data['labels']
        raw_values = raw_data['values']
        i = 0
        for raw_label in raw_labels:
            raw_value = raw_values[i]
            cleaned_data.update({str(raw_label.get_text()): raw_value.get_text()})
            i += 1
        return cleaned_data

    # Private Method to scrap data from MarketWatch's web page
    def _scrap_financial_key_data(self):
        raw_data_obj = {}
        try:
            driver = webdriver.PhantomJS(desired_capabilities=self._dcap)
        except:
            print('***SETUP ERROR: The PhantomJS Web Driver is either not configured or incorrectly configured!***')
            sys.exit(1)
        driver.get(self._base_url + self.ticker)
        i = 0
        while i < self._retries:
            try:
                time.sleep(3)
                html = driver.page_source
                soup = BeautifulSoup(html, "html.parser")
                labels = soup.find_all('small', class_="kv__label")
                values = soup.find_all('span', class_="kv__primary")
                if labels and values:
                    raw_data_obj.update({'labels': labels})
                    raw_data_obj.update({'values': values})
                    break
                else:
                    i += 1
            except:
                i += 1
                continue
        if i == self._retries:
            print('Please check your internet connection!\nUnable to connect...')
            sys.exit(1)
        driver.service.process.send_signal(signal.SIGTERM)
        driver.quit()
        return raw_data_obj

    # Public Method to return a Stock's Key Data Object
    def get_stock_key_data(self):
        raw_data = self._scrap_financial_key_data()
        return self._cleaned_key_data_object(raw_data)


# Script's Main Process to test MarketWatchETL('TICKER')
if __name__ == '__main__':

    # Run financial key data extracts for Microsoft, Apple, and Wells Fargo
    msft_key_data = MarketWatchETL('MSFT').get_stock_key_data()
    aapl_key_data = MarketWatchETL('AAPL').get_stock_key_data()
    wfc_key_data = MarketWatchETL('WFC').get_stock_key_data()

    # Print result dictionaries
    print(msft_key_data.items())
    print(aapl_key_data.items())
    print(wfc_key_data.items())

Какие выходы:

dict_items([('Rev. per Employee', '$841.03K'), ('Short Interest', '44.63M'), ('Yield', '1.53%'), ('Market Cap', '$831.23B'), ('Open', '$109.27'), ('EPS', '$2.11'), ('Shares Outstanding', '7.68B'), ('Ex-Dividend Date', 'Aug 15, 2018'), ('Day Range', '108.51 - 109.64'), ('Average Volume', '25.43M'), ('Dividend', '$0.42'), ('Public Float', '7.56B'), ('P/E Ratio', '51.94'), ('% of Float Shorted', '0.59%'), ('52 Week Range', '72.05 - 111.15'), ('Beta', '1.21')])
dict_items([('Rev. per Employee', '$2.08M'), ('Short Interest', '42.16M'), ('Yield', '1.34%'), ('Market Cap', '$1.04T'), ('Open', '$217.15'), ('EPS', '$11.03'), ('Shares Outstanding', '4.83B'), ('Ex-Dividend Date', 'Aug 10, 2018'), ('Day Range', '216.33 - 218.74'), ('Average Volume', '24.13M'), ('Dividend', '$0.73'), ('Public Float', '4.82B'), ('P/E Ratio', '19.76'), ('% of Float Shorted', '0.87%'), ('52 Week Range', '149.16 - 219.18'), ('Beta', '1.02')])
dict_items([('Rev. per Employee', '$384.4K'), ('Short Interest', '27.44M'), ('Yield', '2.91%'), ('Market Cap', '$282.66B'), ('Open', '$58.87'), ('EPS', '$3.94'), ('Shares Outstanding', '4.82B'), ('Ex-Dividend Date', 'Aug 9, 2018'), ('Day Range', '58.76 - 59.48'), ('Average Volume', '18.45M'), ('Dividend', '$0.43'), ('Public Float', '4.81B'), ('P/E Ratio', '15.00'), ('% of Float Shorted', '0.57%'), ('52 Week Range', '49.27 - 66.31'), ('Beta', '1.13')])

Единственный дополнительный шаг, который вам нужно сделать перед запуском, - это установить и настроить веб-драйвер PhantomJS в своих средах развертывания. Если вам необходимо автоматизировать развертывание веб-скребка, например для этого вы можете написать сценарий установщика оболочки bash / power для предварительной настройки PhantomJS вашей среды.

Некоторые ресурсы для установки и настройки PhantomJS:

Исполняемые файлы установки Windows / Mac PhantomJS

Руководство по установке Debian Linux PhantomJS

RHEL PhantomJS Руководство по установке

Если вы считаете это неполным ответом, то я заранее извиняюсь. Я просто сомневаюсь в практичности и даже в возможности сборки Cookie, как я предлагал в вашем предыдущем посте.

Я думаю, что другой практической возможностью здесь является написание Scrapy Crawler, который я могу попытаться сделать для вас завтра вечером, если хотите.

Надеюсь, это все поможет!

...