使用Python与Selenium和BeautifulSoup4在Javascript加载页面上的所有元素后如何获取数据?

问题描述 投票:0回答:1

我正在尝试从沙盒网站抓取数据,只是为了练习并开始使用 python 抓取网络数据。 我已经成功地使用基础知识提取了大量数据,但是我发现了一个在初始页面加载后动态加载的元素。 我的问题是元素加载后如何提取这些数据?

这是我当前正在使用的代码:

import pandas as pd
from bs4 import BeautifulSoup
from selenium import webdriver

options = webdriver.FirefoxOptions()
driver = webdriver.Firefox(options=options)
driver.get('https://sandbox.oxylabs.io/products')

results = []
other_results = []
status = []
content = driver.page_source
soup = BeautifulSoup(content, 'html.parser')

for element in soup.find_all(attrs={'class': 'product-card'}):
    name = element.find('h4')
    if name not in results:
        results.append(name.text)
        
for b in soup.find_all(attrs={'class': 'product-card'}):
# Note the use of 'attrs' to again select an element with the specified class.
    name2 = b.find(attrs={'class': 'price-wrapper'})
    # stock = b.find(attrs={'class': 'price-wrapper'}).find_next_sibling('p')
    # status.append(stock.text)
    other_results.append(name2.text)
    print(b)

df = pd.DataFrame({'Names': results, 'Prices': other_results, 'Stock': status})
df.to_csv('products.csv', index=False, encoding='utf-8')

我想要获取的元素是库存 加载时的示例如下

缺货

但是它不会出现在任何元素的 print(b) 中

我尝试使用 css 选择器和 .find() 但是有两个问题,即缺货商品具有缺货类别,库存商品具有库存类别。 我尝试使用 if 语句手动设置变量是否有库存类,然后将其设置为“有库存”,否则“缺货”,但是由于元素不存在,它总是返回缺货。

javascript python selenium-webdriver web-scraping beautifulsoup
1个回答
0
投票

我绝不会推荐任何人使用selenium进行网络爬行,因为它只适合测试过程。

为了解决如何准确抓取它们,即使是像ajax这样的Javascript生成的内容,也会部分地从API中抓取。所以你需要检查开发工具(F12)->网络并尝试一一检查它们

通过这个方法,我可以找到3个爬行选项

让我们放入一个装饰器来查看实际运行时间

from functools import wraps
import time

def timeit(func):
    @wraps(func)
    def timeit_wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        total_time = end_time - start_time
        print(f'Function {func.__name__}{args} {kwargs} Took {total_time:.4f} seconds')
        return result
    return timeit_wrapper

方法1

使用Requests和BeautifulSoup,想法是获取所有纯HTML并将其解析为BeautifulSoup。在这种情况下,稍后不会生成 Javascript 来显示 2 个字段名称和价格,因此这对于纯 HTML 来说是没问题的

from bs4 import BeautifulSoup
import requests

@timeit
def crawl_using_html():
    # Function crawl_using_html() {} Took 48.6248 seconds
    results = []
    page = 1
    while True:
        url = f'https://sandbox.oxylabs.io/products?page={page}'

        sub_results = []
        content = requests.get(url).content
        soup = BeautifulSoup(content, 'html.parser')

        found_elements = soup.find_all('div', {'class': 'product-card'})

        if len(found_elements) == 0:
            break

        for element in found_elements:
            name = element.find('h4')
            price = element.find('div', {'class': 'price-wrapper'})
            sub_results.append((name.text, price.text))
                
        page += 1
        results.extend(sub_results)
        print(sub_results)

crawl_using_html()

方法2

如果你看网络,你可能会发现来自

https://sandbox.oxylabs.io/_next/data/-0UsSqtBWVEoFjJOOUmuF/products.json
的products.json,我会尝试切换分页,看看它是否可以像方法1一样放入参数页面

所以你甚至不需要使用 BeautifulSoup,代码将如下所示

import json

@timeit
def crawl_using_paging_api():
    # Function crawl_using_paging_api() {} Took 41.0094 seconds
    results = []
    page = 1
    while True:
        url = f'https://sandbox.oxylabs.io/_next/data/-0UsSqtBWVEoFjJOOUmuF/products.json?page={page}'

        sub_results = []
        content = requests.get(url).json()
        
        products = content['pageProps']['products']
        if len(products) == 0:
            break

        for element in products:
            sub_results.append((element['game_name'], f'{element["user_score"]},99 €'))
                
        page += 1
        results.extend(sub_results)
        print(sub_results)

crawl_using_paging_api()

有趣的是,我们在 API 中没有价格,

price
是从
user_score
获取并附加
,99 €

方法3

在玩网络检查时,我看到API正在获取完整的产品而无需分页,因此它可以将爬行时间缩短到不到1秒

@timeit
def crawl_using_full_api():
    # Function crawl_using_full_api() {} Took 0.8606 seconds
    results = []

    url = f'https://scraping-demo-api-json-server.vercel.app/products'

    products = requests.get(url).json()
    
    for element in products:
        results.append((element['game_name'], f'{element["user_score"]},99 €'))
            
    print(results)

crawl_using_full_api()
© www.soinside.com 2019 - 2024. All rights reserved.