用 Beautiful soup 抓取动态内容

Webscraping of dynamic content with Beautiful soup

为了训练我的 python 技能,我试图从“Arbeitsagentur”(https://www.arbeitsagentur.de/jobsuche/) 的网站上删除特定给定工作的空缺职位数量。我使用 firefox 浏览器的 web-developer 检查工具从包含信息的项目中提取文本,例如“12.231 个职位供 Informatiker/in”。我的代码:

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait

content = "https://www.arbeitsagentur.de/jobsuche/suche?angebotsart=1&was=Informatiker%2Fin"
options = Options()
options.add_argument('--headless')
driver = webdriver.Firefox(executable_path="C:/Drivers/geckodriver/geckodriver.exe", options=options)
driver.get(content)
soup = BeautifulSoup(driver.page_source, 'html.parser')
num_jobs = soup.select_one('div[class="h1-zeile-suche-speichern-container-content container-fluid"] h2')
print(num_jobs)
driver.close()

结果我提取了正确的行,但它不包括查询的信息。翻译成英文我得到这个输出:

<h2 _ngcontent-serverapp-c39="" class="h6" id="suchergebnis-h1-anzeige">Jobs for Informatiker/in are loaded</h2>

在 firefox 的网络检查器中,我看到的是:

<h2 id="suchergebnis-h1-anzeige" class="h6" _ngcontent-serverapp-c39="">
12.231 Jobs für Informatiker/in</h2>

我尝试了 WebDriverWait 方法和 driver.implicitly_wait() 来等待网页完全加载但没有成功。 可能这个值是由 js 脚本(?)计算和插入的。因为我不是 Web 开发人员,所以我不知道它是如何工作的,也不知道如何正确提取具有给定作业数量的行。我尝试使用 firefox 开发人员工具的调试器来查看值的计算位置/方式。但大多数脚本只是非常隐晦的一行代码。

(通过正则表达式从字符串/文本行中提取 number/value 完全没有问题)。

非常感谢您的支持或任何有用的提示。

由于内容是动态加载的,所以number of job result只有在某个元素可见后才能解析,那样的话,所有元素都会被加载,你就可以成功解析你想要的数据了。

您也可以增加睡眠时间以加载所有数据,但这是一个糟糕的解决方案。

工作代码-

import time

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager

options = webdriver.ChromeOptions()

# options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920x1080")
options.add_argument("--disable-extensions")

chrome_driver = webdriver.Chrome(
    service=Service(ChromeDriverManager().install()),
    options=options
)


def arbeitsagentur_scraper():
    URL = "https://www.arbeitsagentur.de/jobsuche/suche?angebotsart=1&was=Informatiker%2Fin"
    with chrome_driver as driver:
        driver.implicitly_wait(15)
        driver.get(URL)
        wait = WebDriverWait(driver, 10)
        
        # time.sleep(10) # increase the load time to fetch all element, not advised solution
       
        # wait until this element is visible 
        wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, '.liste-container')))
        
        elem = driver.find_element(By.XPATH,
                                   '/html/body/jb-root/main/jb-jobsuche/jb-jobsuche-suche/div[1]/div/jb-h1zeile/h2')
        print(elem.text)


arbeitsagentur_scraper()

输出-

12.165 Jobs für Informatiker/in

或者,您可以使用他们的 API URL 加载结果。例如:

import json
import requests


api_url = "https://rest.arbeitsagentur.de/jobboerse/jobsuche-service/pc/v4/jobs"

query = {
    "angebotsart": "1",
    "was": "Informatiker/in",
    "page": "1",
    "size": "25",
    "pav": "false",
}

headers = {
    "OAuthAccessToken": "eyJhbGciOiJIUzUxMiJ9.eyAic3ViIjogIklkNFZSNmJoZFpKSjgwQ2VsbHk4MHI4YWpkMD0iLCAiaXNzIjogIk9BRyIsICJpYXQiOiAxNjU0MDM2ODQ1LCAiZXhwIjogMS42NTQwNDA0NDVFOSwgImF1ZCI6IFsgIk9BRyIgXSwgIm9hdXRoLnNjb3BlcyI6ICJhcG9rX21ldGFzdWdnZXN0LCBqb2Jib2Vyc2Vfc3VnZ2VzdC1zZXJ2aWNlLCBhYXMsIGpvYmJvZXJzZV9rYXRhbG9nZS1zZXJ2aWNlLCBqb2Jib2Vyc2Vfam9ic3VjaGUtc2VydmljZSwgaGVhZGVyZm9vdGVyX2hmLCBhcG9rX2hmLCBqb2Jib2Vyc2VfcHJvZmlsLXNlcnZpY2UiLCAib2F1dGguY2xpZW50X2lkIjogImRjZGVhY2JkLTJiNjItNDI2MS1hMWZhLWQ3MjAyYjU3OTg0OCIgfQ.BBkJbJ93fGqQQQGX4-VTzX8P6Twg8Rthq8meXV2WY_CoUmXQWhdgbjkFozP2BJXooSr7yLaTJr7JXEk8hDnCWA",
}

data = requests.get(api_url, params=query, headers=headers).json()

# uncomment to print all data:
# print(json.dumps(data, indent=4))

print(data["maxErgebnisse"])

打印:

12165