首页 > 解决方案 > 无法使用 BS4 从eastbay.com 抓取数据?

问题描述

所以我选择了我感兴趣的品牌,并生成了这个 URL:

https://www.eastbay.com/category/mens/shoes.html?query=%3Arelevance%3Agender%3A200000%3AproductType%3A200005%3Abrand%3AChampion%3Abrand%3AConverse%3Abrand%3AFila%3Abrand%3AJordan%3Abrand%3ANew +Balance%3Abrand%3ANike%3Abrand%3ANike+SB%3Abrand%3APUMA%3Abrand%3AReebok%3Abrand%3ASalomon%3Abrand%3AThe+North+Face%3Abrand%3Atimberland%3Abrand%3AUGG%3Abrand%3AUnder+Armour%3Abrand%3AVans %3Abrand%3Aadidas%3Abrand%3Aadidas+原件&

我正在尝试从此页面中抓取数据,主要是产品页面 URL(具有类的元素的href属性值)。aLink--product

我的第一个问题是,使用 BS4,我无法从网站获取任何数据。

即使运行这个简单的测试片段(忽略大多数导入,它们在主程序中使用),

import requests
import csv
import io
import os
import re
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from datetime import datetime
from bs4 import BeautifulSoup

headers = {
     'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
}

data = requests.get("https://www.eastbay.com/category/sport/casual/mens/shoes.html",headers=headers)
soup = BeautifulSoup(data.text, 'lxml')

x = soup.find('span', attrs={'class': 'primary'}).text.strip()
print(x)

它应该使用类打印第一个span元素中的文本primary(结果应该是Nike Air Force 1 Low),返回错误requests.exceptions.ConnectionError: ('Connection aborted.', OSError("(10060, 'WSAETIMEDOUT')")),这应该表明我的标题没有被主机接受,但是我尝试了许多不同的标题但没有成功。

Nike Air Force 1 Low使用这个简单的代码片段让 Selenium 工作和显示:

driver = webdriver.Chrome()
driver.get("https://www.eastbay.com/category/sport/casual/mens/shoes.html") 
x = driver.find_element_by_xpath("//span[@class='primary']")
print(x.text)
driver.close()

但如果可能的话,我真的更喜欢使用 BS4。有谁知道如何让 BS4 与这个网站一起工作?

我的第二个问题是关于这个网站的分页。在 URL 的底部附近,有一个“加载更多”按钮,该按钮向 API 发出请求,然后加载下一批产品。但是,URL 永远不会改变。谁能帮我迭代批次以便获取所有产品,而不仅仅是前 60 个产品?

我尝试了这样的方法,从另一个网站的脚本修改,该脚本在 URL 中也没有分页:

url = "https://www.eastbay.com/category/mens/shoes.html?query=%3Arelevance%3Agender%3A200000%3AproductType%3A200005%3Abrand%3AChampion%3Abrand%3AConverse%3Abrand%3AFila%3Abrand%3AJordan%3Abrand%3ANew+Balance%3Abrand%3ANike%3Abrand%3ANike+SB%3Abrand%3APUMA%3Abrand%3AReebok%3Abrand%3ASalomon%3Abrand%3AThe+North+Face%3Abrand%3ATimberland%3Abrand%3AUGG%3Abrand%3AUnder+Armour%3Abrand%3AVans%3Abrand%3Aadidas%3Abrand%3Aadidas+Originals"

qsp = {
    'currentPage': 1,
    'pageSize': 100,
    'timestamp': 3
}

container = []


for page_content in range(0,1500,60):
    qsp['currentPage'] = page_content
    res = requests.get(url,params=qsp,headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"})
    soup = BeautifulSoup(res.text, 'lxml')
    for item in soup.select(".c-product-card a"):
        container.append("https://www.eastbay.com"+item['href'])

    for items in soup.select("script"):
        sauce = BeautifulSoup(items.text,"lxml")
        for elem in sauce.select(".c-product-card a"):
            container.append("https://www.eastbay.com"+elem['href'])

print(container)

但这会导致一个空列表或只是卡住。

我已经检查了 Chrome 开发工具中网络选项卡中的 XHR 和 Fetch 选项卡,我可以看到单击“加载更多”按钮正在发出什么样的请求,但是在传递查询字符串参数以进行迭代时我一无所知超过批次产品。

任何帮助深表感谢。

标签: pythonseleniumweb-scrapingbeautifulsoup

解决方案


你对 XHR 的想法是正确的。以下是获取它的方法,然后遍历该 json 格式以打印出所需的输出:

import requests

url = 'https://www.eastbay.com/api/products/search'

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'}

payload = {
'query': ':relevance:sport:Casual:gender:200000:productType:200005',
'currentPage': '1',
'pageSize': '200',
'timestamp': '4'}

jsonData = requests.get(url, headers=headers, params=payload).json()

totalPages = jsonData['pagination']['totalPages']
totalResults = jsonData['pagination']['totalResults']

print ('%s total results to aquire' %totalResults)


for page in range(1,totalPages+1):
    payload = {
            'query': ':relevance:sport:Casual:gender:200000:productType:200005',
            'currentPage': page,
            'pageSize': '200',
            'timestamp': '4'}


    jsonData = requests.get(url, headers=headers, params=payload).json()

    try:
        for product in jsonData['products']:
            print (product['name'])
    except:
        print ('Products not found on this request')

推荐阅读