#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}')
Я планирую добавить к этому что-то еще, чтобы его можно было использовать для любого продукта. Если кто-нибудь знает о пользовательском агенте или о том, как я могу убедиться, что он загружает все данные, даже если есть новая продажа, дайте мне знать. Поскольку я жестко запрограммировал количество результатов в программе, если будет другая продажа, я пропущу это при следующем запуске скрипта.