Google Jobs 动态呈现列表并本地化结果,因此简单的 HTTP 请求很少返回可用的数据。最可靠的方法是使用处理 JavaScript 渲染和地理定位的抓取 API,然后将您需要的字段解析为 CSV。
您将构建什么:
- 每个查询和位置的 CSV 文件,包含职位、公司、位置、日期、工资、来源和共享 URL。
- 可跨多个地理位置同时运行多个查询的可扩展管道。
方法 1 — Oxylabs Web Scraper API(异步、可扩展)
Oxylabs 的 Web Scraper API(Google Jobs 来源)运行具有地理定位和自定义解析器的无头浏览器,因此您可以收到结构化字段而不是原始 HTML。
你需要什么
- Oxylabs 帐户和 API 用户凭据(用户名/密码)。在 Oxylabs 仪表板中创建一个帐户,开始免费试用,然后从仪表板凭据页面复制您的 API 用户和密码。
- 您机器上的 Python 3.11+。
步骤一:创建一个 Oxylabs 帐户并从仪表板 (Oxylabs) 复制您的 API 用户名和密码。
步骤2:如果尚未安装,请在系统上安装 Python 3.11 或更高版本。
步骤3:安装异步抓取所需的库。
pip install aiohttp asyncio pandas第4步:创建一个名为payload.json带有 Google Jobs 的解析规则(Oxylabs 自定义解析器)。
{
"source": "google",
"url": null,
"geo_location": null,
"user_agent_type": "desktop",
"render": "html",
"parse": true,
"parsing_instructions": {
"jobs": {
"_fns": [
{ "_fn": "xpath", "_args": ["//div[@class='nJXhWc']//ul/li"] }
],
"_items": {
"job_title": { "_fns": [{ "_fn": "xpath_one", "_args": [".//div[@class='BjJfJf PUpOsf']/text()"] }] },
"company_name": { "_fns": [{ "_fn": "xpath_one", "_args": [".//div[@class='vNEEBe']/text()"] }] },
"location": { "_fns": [{ "_fn": "xpath_one", "_args": [".//div[@class='Qk80Jf'][1]/text()"] }] },
"date": { "_fns": [{ "_fn": "xpath_one", "_args": [".//div[@class='PuiEXc']//span[@class='LL4CDc' and contains(@aria-label, 'Posted')]/span/text()"] }] },
"salary": { "_fns": [{ "_fn": "xpath_one", "_args": [".//div[@class='PuiEXc']//div[contains(@class,'I2Cbhb') and contains(@class,'bSuYSc')]//span[@aria-hidden='true']/text()"] }] },
"posted_via": { "_fns": [{ "_fn": "xpath_one", "_args": [".//div[@class='Qk80Jf'][2]/text()"] }] },
"URL": { "_fns": [{ "_fn": "xpath_one", "_args": [".//div[@data-share-url]/@data-share-url"] }] }
}
}
}
}第5步:创建一个名为的 Python 文件jobs_oxylabs.py.
第6步:粘贴导入并加载凭据 (Oxylabs)。
import asyncio
import aiohttp
import json
import pandas as pd
from aiohttp import ClientSession, BasicAuth
# Replace with your Oxylabs API username/password from the dashboard
OXY_USER = "USERNAME"
OXY_PASS = "PASSWORD"
credentials = BasicAuth(OXY_USER, OXY_PASS)
# Load parsing payload
with open("payload.json", "r") as f:
PAYLOAD = json.load(f)第7步:添加辅助函数来提交作业、轮询状态和获取结果(Oxylabs 推拉端点)。
async def submit_job(session: ClientSession, payload: dict) -> str:
async with session.post("https://data.oxylabs.io/v1/queries", auth=credentials, json=payload) as resp:
data = await resp.json()
return data["id"]
async def check_status(session: ClientSession, job_id: str) -> str:
async with session.get(f"https://data.oxylabs.io/v1/queries/{job_id}", auth=credentials) as resp:
data = await resp.json()
return data["status"]
async def fetch_jobs(session: ClientSession, job_id: str) -> list:
async with session.get(f"https://data.oxylabs.io/v1/queries/{job_id}/results", auth=credentials) as resp:
data = await resp.json()
return data["results"][0]["content"]["jobs"]步骤8:添加一个函数,用于将每个查询和位置的解析作业写入 CSV。
async def save_csv(query: str, location: str, jobs: list) -> None:
rows = []
for j in jobs:
rows.append({
"Job title": j.get("job_title"),
"Company name": j.get("company_name"),
"Location": j.get("location"),
"Date": j.get("date"),
"Salary": j.get("salary"),
"Posted via": j.get("posted_via"),
"URL": j.get("URL"),
})
df = pd.DataFrame(rows)
filename = f"{query}_jobs_{location.replace(',', '_').replace(' ', '_')}.csv"
await asyncio.to_thread(df.to_csv, filename, index=False)第9步:定义用于设置 Google Jobs URL 和地理位置、提交工作、等待完成并保存结果的协程 (Oxylabs)。
async def scrape_jobs(session: ClientSession, query: str, country_code: str, location: str) -> None:
url = f"https://www.google.com/search?q={query}&ibp=htl;jobs&hl=en&gl={country_code}"
PAYLOAD["url"] = url
PAYLOAD["geo_location"] = location
job_id = await submit_job(session, PAYLOAD)
# Give the backend time to render before polling
await asyncio.sleep(12)
while True:
status = await check_status(session, job_id)
if status == "done":
break
if status == "failed":
print(f"Job {job_id} failed for {query} @ {location}.")
return
await asyncio.sleep(5)
jobs = await fetch_jobs(session, job_id)
await save_csv(query, location, jobs)第10步:添加main()同时运行多个查询和位置。
URL_QUERIES = ["developer", "chef", "manager"]
LOCATIONS = {
"US": ["California,United States", "Virginia,United States", "New York,United States"],
"GB": ["United Kingdom"],
"DE": ["Germany"]
}
async def main():
async with aiohttp.ClientSession() as session:
tasks = []
for cc, locs in LOCATIONS.items():
for loc in locs:
for q in URL_QUERIES:
tasks.append(asyncio.ensure_future(scrape_jobs(session, q, cc, loc)))
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(main())
print("Completed.")第11步:运行脚本并验证是否根据查询和位置创建了 CSV 文件。
笔记:
- Google 招聘信息共享 URL 只能从抓取过程中使用的同一国家/地区的 IP 打开;打开它们时使用匹配的代理/VPN。
- 使用
Ctrl+Shift+I在 Windows 或Option+Command+I如果您想扩展解析,请在 macOS 上检查选择器。
方法 2 — SerpApi Google Jobs API(简单的 REST、过滤器和半径)
SerpApi 的google_jobs引擎返回带有可选过滤器(如语言、国家/地区和半径)的结构化列表。
你需要什么
- SerpApi 帐户和 API 密钥。在 serpapi.com 上注册,打开仪表板,然后从“API 密钥”页面复制您的 API 密钥。
- Python 3.9+ 和
requests和pandas包。
步骤一:创建 SerpApi 帐户并从仪表板 (SerpApi) 复制您的 API 密钥。
步骤2:安装依赖项。
pip install requests pandas步骤3:发送您的第一个 Google Jobs 请求并将结果写入 CSV (SerpApi)。
import requests
import pandas as pd
API_KEY = "YOUR_SERPAPI_KEY"
params = {
"engine": "google_jobs",
"q": "developer new york",
"hl": "en",
"gl": "us",
"api_key": API_KEY,
# Optional: radius in kilometers (Google may not strictly enforce)
"lrad": 25
}
r = requests.get("https://serpapi.com/search.json", params=params)
r.raise_for_status()
data = r.json()
rows = []
for j in data.get("jobs_results", []):
rows.append({
"Job title": j.get("title"),
"Company name": j.get("company_name"),
"Location": j.get("location"),
"Posted via": j.get("via"),
"Share link": j.get("share_link"),
"Description": j.get("description")
})
pd.DataFrame(rows).to_csv("jobs_serpapi.csv", index=False)第4步:使用分页next_page_token从响应中获取其他页面(SerpApi)。
def fetch_all(params):
out = []
while True:
res = requests.get("https://serpapi.com/search.json", params=params).json()
out.extend(res.get("jobs_results", []))
token = res.get("serpapi_pagination", {}).get("next_page_token")
if not token:
break
params["next_page_token"] = token
return out
jobs = fetch_all(params)尖端:
- 使用
location或者uule模拟城市级搜索;避免将两者组合在同一个请求中。 - 过滤器可以作为
udsJSON 中的字符串;重复使用过滤器uds改进结果的新请求中的价值。
方法 3 — 使用 Selenium 的免费本地刮刀(最适合小型测试)
无头浏览器可以滚动 Google 职位列表并使用强大的选择器提取字段。这对于原型设计来说很实用,但需要维护,并且可能会大量攻击反机器人系统。
你需要什么
- Google Chrome 和匹配的 ChromeDriver 二进制文件。
- Python 3.9+ 与
selenium,beautifulsoup4(可选),以及pandas.
步骤一:下载与您的 Chrome 版本匹配的 ChromeDriver 并将其添加到您的 PATH 中。
步骤2:安装 Selenium 和 pandas。
pip install selenium pandas步骤3:启动 Chrome,并带有减少自动化指纹的选项。
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--window-size=1920,1080")
options.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(options=options)第4步:打开根据您的查询和国家/地区代码构建的 Google 职位网址。
query = "developer"
country = "us"
url = f"https://www.google.com/search?q={query}&ibp=htl;jobs&hl=en&gl={country}"
driver.get(url)第5步:滚动作业列表容器以加载更多结果。
import time
from selenium.webdriver.common.by import By
loaded = 0
while True:
cards = driver.find_elements(By.XPATH, "//div[@class='nJXhWc']//ul/li")
if len(cards) == loaded:
break
loaded = len(cards)
driver.execute_script("arguments[0].scrollIntoView({block: 'end'});", cards[-1])
time.sleep(1.5)第6步:使用 XPath 提取稳定字段;如果可用,请使用基于图标的选择器来获取发布日期和工资。
from selenium.common.exceptions import NoSuchElementException
rows = []
for li in driver.find_elements(By.XPATH, "//div[@class='nJXhWc']//ul/li//div[@role='treeitem']/div/div"):
def txt(xp):
try:
return li.find_element(By.XPATH, xp).get_attribute("innerText")
except NoSuchElementException:
return None
def attr(xp, name):
try:
return li.find_element(By.XPATH, xp).get_attribute(name)
except NoSuchElementException:
return None
rows.append({
"Job title": txt("./div[2]"),
"Company name": txt("./div[4]/div/div[1]"),
"Location": txt("./div[4]/div/div[2]"),
"Source": txt("./div[4]/div/div[3]"),
"Posted": txt(".//*[name()='path'][contains(@d,'M11.99')]/ancestor::div[1]"),
"Full/Part": txt(".//*[name()='path'][contains(@d,'M20 6')]/ancestor::div[1]"),
"Salary": txt(".//*[name()='path'][@fill-rule='evenodd']/ancestor::div[1]"),
"Logo src": attr("./div[1]//img", "src")
})第7步:将提取的数据保存到 CSV。
import pandas as pd
pd.DataFrame(rows).to_csv("jobs_selenium.csv", index=False)警告:
- 预计布局变化;定期重新验证选择器。
- 限制请求并遵守站点条款以避免阻塞。
选择一种方法
对于生产和规模,Oxylabs 的异步管道可以跨多个查询和位置可靠地返回已解析的作业,从而减少工程时间和故障。 SerpApi 可以快速集成较小的脚本,并允许您添加半径和过滤器令牌。 Selenium 最适合快速实验或无法使用第三方 API 时。
快速维护技巧:将查询列表和位置字典保存在单独的 JSON 或 YAML 文件中,以便非开发人员可以更新搜索范围而无需接触代码。
