selnium и beutifulsoup scraper очень противоречивы

#python #api #selenium #web-scraping #beautifulsoup

#python #API #селен #очистка веб-страниц #beautifulsoup

Вопрос:

Я создал скребок для страницы на сайте stockx. Программа удаляет данные о продажах из всплывающей таблицы модели. Анализирует цену продажи, складывает их все вместе и дает среднюю цену продажи. Проблема в том, что программа выдает диапазон средних значений каждый раз, когда я ее запускаю.

 from bs4 import BeautifulSoup
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium import webdriver
import time
from selenium.webdriver.common.keys import Keys 
from itertools import islice

PATH = "C:Program Files (x86)chromedriver.exe"

driver = webdriver.Chrome(PATH)
driver.get("https://stockx.com/supreme-patchwork-mohair-cardigan-multicolor")
time.sleep(3)
driver.find_element_by_xpath('//*[@id="root"]/div[1]/div[2]/div[2]/div[9]/div/div/div/div[2]/div/div[2]/div/div/button').click()
driver.find_element_by_xpath('//*[@id="root"]/div[1]/div[2]/div[2]/div[9]/div/div/div/div[2]/div/div[1]/div[2]/a').click()
time.sleep(3)

while EC.element_to_be_clickable((By.LINK_TEXT,'Load More')):
    try:
        driver.find_element_by_css_selector('body > div:nth-child(55) > div > div > div > div.modal-body > div > button').click()
    except Exception as e:
        print(e)
        break
    
src = driver.page_source
soup = BeautifulSoup(src, features='html.parser')
table = soup.table
table_rows = table.find_all('tr')
raw_price_data = list()
bs = list()
for row in islice(table_rows, 1, None):
    td = row.find_all('td')[1]
    raw_price_data.append(td.text[1:])
raw_price_data = list(map(int, raw_price_data))
Sum = sum(raw_price_data)
avg = Sum / len(raw_price_data)
total_sales = len(raw_price_data)

avg = round(avg, 2)
print(f'Total Sales:{total_sales}')
print(f'Average Profit:{avg}')
  

Недавно я добавил общий список продаж, чтобы узнать, сколько строк очищает beutifulsoup, и каждый раз он отличается, как я и думал.

Общий объем продаж: 1390 Средняя прибыль: 402,29

Общий объем продаж: 990 Средняя прибыль: 400.05

Общий объем продаж: 2270 Средняя прибыль: 407,36

Вот некоторые результаты после запуска программы, все разные. Я новичок в python, и я очень признателен за любую помощь в создании веб-страниц.

Это то, что я смог закодировать для использования недокументированного API. Переменная API, которую я нашел, используя вкладку network в инструментах разработки. ПОЛЕЗНАЯ НАГРУЗКА — это параметры запроса для того же запроса, который я нашел. Хотя я все еще не мог получить нужные мне данные в python, только коды ошибок.

 import requests
from  urllib.parse import urlencode


headers = {
    'authority': 'stockx.com',
    'appos': 'web',
    'x-requested-with': 'XMLHttpRequest',
    'authorization': '',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
    'appversion': '0.1',
    'accept': '*/*',
    'sec-fetch-site': 'same-origin',
    'sec-fetch-mode': 'cors',
    'sec-fetch-dest': 'empty',
    'referer': 'https://stockx.com/supreme-patchwork-mohair-cardigan-multicolor',
    'accept-language': 'en-US,en;q=0.9',
    'if-none-match': 'W/^\^c3f-9c/EjYGTDiuj1w0OlHbjycsHHYU^\^',
}

PAYLOAD = {
    "state": "480",
    "currency": "USD",
    "limit": 203,
    "page": "1",
    "sort": "createdAt",
    "order": "DESC",
    "country": "US"
}

api = 'https://stockx.com/api/products/509c6166-53d4-49bf-9221-fc10cb298911/chart'
response = requests.get(f'{api}{urlencode(PAYLOAD)}', headers=headers)
print(response)
  

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

1. у меня есть несколько вопросов, не могли бы вы помочь? @baduker

2. Как вы нашли API, где вы искали информацию для подключения?@baduker

3. вы использовали неофициальный api?

4. Я нашел API, проверив сетевой трафик в инструменте разработчика моего браузера. Это неофициально? Ну, это то, что они используют в своем бэкэнде, так что нет, но это не документировано и открыто, если это то, что вы подразумеваете под «официальным» API. Затем мне пришлось выяснить, откуда берутся эти символы в URL-адресе API. Они совпали с артикулом, на который я наткнулся в исходном коде страницы. Затем соедините все это вместе, и вот оно.

5. Итак, я провел еще несколько исследований по поиску недокументированных API-интерфейсов и думаю, что я на правильном пути, я нашел запрос API на вкладке сети инструментов разработчика, я думаю. Немного поиграл с ним, чтобы выяснить, какие параметры мне нужны для загрузки всех данных в таблице из API на вкладку браузера. @baduker

Ответ №1:

Как насчет того, чтобы отказаться от обоих selenium и BeautifulSoup и перейти на чистый requests ?

Как? Ну, есть API для той веб-страницы, которую вы очищаете. Все, что вам нужно, это sku номер продукта. Как вы это получаете?

Вы просматриваете исходный код страницы продукта только для того, чтобы обнаружить, что есть куча <script> тегов, которые содержат что-то похожее на JSON данные. Здорово, верно?

Кроме того, вы понимаете sku , что находится в одном из этих тегов, и он всегда находится между значениями model и color . Почему бы и нет regex ?

Затем поместите sku в API URL-адрес, проанализируйте ответ и вычислите среднее значение.

Собрать все это вместе:

 import re
from datetime import datetime
from urllib.parse import urlencode

import requests


PRODUCT_URL = "https://stockx.com/supreme-brushed-mohair-cardigan-black"
PRODUCT_NAME = " ".join(i.title() for i in PRODUCT_URL.split('/')[-1].split('-'))

PAYLOAD = {
    "start_date": "all",
    "end_date": "2020-10-24",
    "intervals": 100,
    "format": "highstock",
    "currency": "USD"
}
HEADERS = {
    "referer": PRODUCT_URL,
    "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
    "x-requested-with": "XMLHttpRequest",
}


product_page = requests.get(PRODUCT_URL, headers=HEADERS).text
product_sku = "".join(re.findall(r'"sku":"(. )","color', product_page))

api_url = f"https://stockx.com/api/products/{product_sku}/chart?"
response = requests.get(f"{api_url}{urlencode(PAYLOAD)}", headers=HEADERS).json()

print(f"{response['title']['text']} for {PRODUCT_NAME}:")
series = response["series"][0]["data"]
for item in series:
    timestamp, price = item
    human_readable_date = datetime.fromtimestamp(int(timestamp / 1000))
    print(f"{human_readable_date} - {price}")

print("-" * 25)
print(f"Average: {sum(i[1] for i in series) / len(series)} {PAYLOAD['currency']}")

  

Это выводит:

 Average price over time for Supreme Brushed Mohair Cardigan Black:
2020-10-22 17:18:19 - 250
2020-10-22 17:49:13 - 250
2020-10-22 18:20:07 - 250
2020-10-22 18:51:01 - 250
2020-10-22 19:21:56 - 250
2020-10-22 19:52:50 - 250
2020-10-22 20:23:44 - 250
2020-10-22 20:54:38 - 250
2020-10-22 21:25:33 - 250
2020-10-22 21:56:27 - 250
2020-10-22 22:27:21 - 250
2020-10-22 22:58:15 - 250
2020-10-22 23:29:10 - 250
2020-10-23 00:00:04 - 250
2020-10-23 00:30:58 - 250
2020-10-23 01:01:53 - 200
2020-10-23 01:32:47 - 219
2020-10-23 02:03:41 - 219
2020-10-23 02:34:35 - 219
2020-10-23 03:05:30 - 219
2020-10-23 03:36:24 - 219
2020-10-23 04:07:18 - 219
2020-10-23 04:38:12 - 219
2020-10-23 05:09:07 - 219
2020-10-23 05:40:01 - 219
2020-10-23 06:10:55 - 219
2020-10-23 06:41:50 - 219
2020-10-23 07:12:44 - 219
2020-10-23 07:43:38 - 219
2020-10-23 08:14:32 - 219
2020-10-23 08:45:27 - 219
2020-10-23 09:16:21 - 219
2020-10-23 09:47:15 - 219
2020-10-23 10:18:09 - 219
2020-10-23 10:49:04 - 219
2020-10-23 11:19:58 - 219
2020-10-23 11:50:52 - 219
2020-10-23 12:21:46 - 219
2020-10-23 12:52:41 - 219
2020-10-23 13:23:35 - 219
2020-10-23 13:54:29 - 219
2020-10-23 14:25:24 - 219
2020-10-23 14:56:18 - 219
2020-10-23 15:27:12 - 257
2020-10-23 15:58:06 - 257
2020-10-23 16:29:01 - 315
2020-10-23 16:59:55 - 315
2020-10-23 17:30:49 - 315
2020-10-23 18:01:43 - 315
2020-10-23 18:32:38 - 315
2020-10-23 19:03:32 - 315
2020-10-23 19:34:26 - 315
2020-10-23 20:05:21 - 315
2020-10-23 20:36:15 - 315
2020-10-23 21:07:09 - 315
2020-10-23 21:38:03 - 315
2020-10-23 22:08:58 - 315
2020-10-23 22:39:52 - 315
2020-10-23 23:10:46 - 315
2020-10-23 23:41:40 - 315
2020-10-24 00:12:35 - 315
2020-10-24 00:43:29 - 315
2020-10-24 01:14:23 - 315
2020-10-24 01:45:18 - 315
2020-10-24 02:16:12 - 315
2020-10-24 02:47:06 - 315
2020-10-24 03:18:00 - 315
2020-10-24 03:48:55 - 315
2020-10-24 04:19:49 - 315
2020-10-24 04:50:43 - 315
2020-10-24 05:21:37 - 315
2020-10-24 05:52:32 - 315
2020-10-24 06:23:26 - 315
2020-10-24 06:54:20 - 315
2020-10-24 07:25:14 - 315
2020-10-24 07:56:09 - 277
2020-10-24 08:27:03 - 277
2020-10-24 08:57:57 - 277
2020-10-24 09:28:52 - 277
2020-10-24 09:59:46 - 277
2020-10-24 10:30:40 - 277
2020-10-24 11:01:34 - 277
2020-10-24 11:32:29 - 277
2020-10-24 12:03:23 - 277
2020-10-24 12:34:17 - 277
2020-10-24 13:05:11 - 277
2020-10-24 13:36:06 - 277
2020-10-24 14:07:00 - 277
2020-10-24 14:37:54 - 277
2020-10-24 15:08:49 - 277
2020-10-24 15:39:43 - 277
2020-10-24 16:10:37 - 277
2020-10-24 16:41:31 - 277
2020-10-24 17:12:26 - 277
2020-10-24 17:43:20 - 277
2020-10-24 18:14:14 - 277
2020-10-24 18:45:08 - 277
2020-10-24 19:16:03 - 277
2020-10-24 19:46:57 - 277
2020-10-24 20:17:51 - 277
-------------------------
Average: 267.52 USD
  

Bouns:

Это работает с любым URL-адресом продукта! XD

Редактировать:

Чтобы получить ответ из activity конечной точки, попробуйте следующее:

 import re

import requests
from urllib.parse import urlencode


PRODUCT_URL = "https://stockx.com/supreme-brushed-mohair-cardigan-black"
PRODUCT_NAME = " ".join(i.title() for i in PRODUCT_URL.split('/')[-1].split('-'))


HEADERS = {
    "referer": PRODUCT_URL,
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
    "x-requested-with": 'XMLHttpRequest',
}

PAYLOAD = {
    "state": "480",
    "currency": "USD",
    "limit": 10,
    "sort": "createdAt",
    "order": "DESC",
    "country": "US"
}


product_page = requests.get(PRODUCT_URL, headers=HEADERS).text
product_sku = "".join(re.findall(r'"sku":"(. )","color', product_page))

api_url = f"https://stockx.com/api/products/{product_sku}/activity?"
response = requests.get(f'{api_url}{urlencode(PAYLOAD)}', headers=HEADERS).json()

for item in response:
    print(f"{item['chainId']} - shoe size: {item['shoeSize']} at {item['amount']} {item['localCurrency']}")
  

Вывод:

 13451189134512711854 - shoe size: M at 250.6639 USD
13454493613262000326 - shoe size: XL at 305 USD
13454719168825677535 - shoe size: M at 250.451 USD
13454432070832901351 - shoe size: XL at 321.4601 USD
13454370874521531956 - shoe size: XL at 315 USD
13454370577625857582 - shoe size: XL at 320 USD
13450796013705700403 - shoe size: M at 240 USD
...
  

Ответ №2:

Итак, я, наконец, понял. Я решил выбросить beautiful soup и selenium и просто использовать request и urllib после прочтения сообщения @badukers еще раз спасибо. Несмотря на то, что метод badukers помог мне встать на правильный путь, API, который он использовал, не давал мне данных, которые я искал для средней прибыли. После некоторых исследований недокументированных API-интерфейсов я смог просмотреть инструменты разработчика, чтобы найти запрос для API, в котором мне нужен /activity, а не /chart API. После поиска правильного API мне пришлось выяснить, как использовать аргументы, поэтому я играл с URL, пока не получил все 203 продажи. На тот момент было легко перенести это на python, просто нужно было убедиться, что я выбрал правильные аргументы и необходимый заголовок. Одна вещь, в которой я все еще не уверен, это то, что означает пользовательский агент и почему сценарий завершается с ошибкой, если он не включен, но все остальное можно исключить.

Вот мой код для сбора общей средней прибыли для элемента кардигана, который я искал.

 import requests
from urllib.parse import urlencode


API_URL = 'https://stockx.com/api/products/509c6166-53d4-49bf-9221-fc10cb298911/activity?'


PAYLOAD = {
    "state": "480",
    "currency": "USD",
    "limit": 203,
    "page": "1",
    "sort": "createdAt",
    "order": "DESC",
    "country": "US"
}

HEADERS = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
    'referer': 'https://stockx.com/supreme-patchwork-mohair-cardigan-multicolor',
}

response = requests.get(f'{API_URL}'   urlencode(PAYLOAD), headers=HEADERS).json()


ProductActivity = response['ProductActivity']
amount = [i['amount'] for i in ProductActivity]
tprofit = list()
for price in amount:
    profit = price - 188
    tprofit.append(profit)
    print(f'Sale Price:Profit--------------{price}:{profit}')
print('-'*40)
ave = sum(tprofit)/len(amount)
print(f'Raw Sale Average--------------{ave}')
  

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