如何使用 Python 抓取 Indeed 职位列表

评论: 0

对于求职者、雇主或任何监测就业市场趋势的个人来说,搜索 Indeed 的可用职位列表可以提供有用的信息。在本教程中,我们将结合 Playwright(用于网络搜刮)和 lxml(用于 HTML 内容解析)来收集职位的详细信息,包括职位名称、招聘公司名称、地点、职位描述和职位发布链接,最后将信息保存到 CSV 文件中,以展示搜索结果。

前提条件

要成功执行搜索,需要安装以下 Python 库。

用于浏览器自动化的 Playwright:


pip install playwright

lxml 用于解析 HTML:


pip install lxml

pandas 用于将数据保存到 CSV 文件:


pip install pandas

安装 Playwright 浏览器

安装 Playwright 后,运行此命令安装必要的浏览器二进制文件:


playwright install

第 1 步:设置 Playwright 以进行网络刮擦

Playwright 允许您自动执行网页浏览器并与之交互。我们首先设置 Playwright 以启动 Chromium 浏览器、访问网页并提取其内容。在这里,我们还可以通过 playwright 传递代理。

为什么使用代理?

网站通常会设置速率限制或反搜索措施,以阻止来自同一 IP 地址的重复请求。代理允许您这样做:

  • 避免 IP 阻断:通过不同的 IP 分配您的请求,以避免被发现。
  • 绕过地理位置:访问可能受地理位置限制的职位列表。
  • 匿名:隐藏您的实际 IP,在搜索过程中保持匿名。

import asyncio
from playwright.async_api import async_playwright

async def get_page_content(url):
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False,
            proxy = {
                'server': '',
                'username': '',
                'password': ''
            }
        )  # 标题浏览器
        page = await browser.new_page()
        await page.goto(url)
        
        # 提取页面内容
        content = await page.content()
        
        await browser.close()  # 完成后关闭浏览器
        return content

在这段代码中,async_playwright 会启动一个浏览器,导航到指定的 URL 并获取页面内容。

使用 lxml 解析 HTML 内容

接下来,我们将解析页面内容以提取有意义的数据。之所以使用 lxml,是因为它为使用 XPath 解析和查询 HTML 内容提供了强大的支持。


from lxml import html

def parse_job_listings(content):
    # 解析 HTML 内容
    parser = html.fromstring(content)
    
    # 使用 XPath 提取每个招聘信息
    job_posting = parser.xpath('//ul[@class="css-zu9cdh eu4oa1w0"]/li')
    
    jobs_data = []
    for element in job_posting[:-1]:  # 如果是广告或无关内容,则跳过最后一个元素
        title = ''.join(element.xpath('.//h2/a/span/@title'))
        if title:
            link = ''.join(element.xpath('.//h2/a/@href'))
            location = ''.join(element.xpath('.//div[@data-testid="text-location"]/text()'))
            description = ', '.join(element.xpath('.//div[@class="css-9446fg eu4oa1w0"]/ul//li/text()'))
            company_name = ''.join(element.xpath('.//span[@data-testid="company-name"]/text()'))

            # 将提取的数据添加到 jobs_data 列表中
            jobs_data.append({
                'Title': title,
                'Link': f"https://www.indeed.com{link}",
                'Location': location,
                'Description': description,
                'Company': company_name
            })
    
    return jobs_data

步骤 2:搜索职位列表

现在,我们已经设置了浏览器自动化和解析步骤,让我们将它们结合起来,从 Indeed 页面抓取职位列表。

说明

  • get_page_content(url):使用 Playwright 抓取页面内容。
  • parse_job_listings(content):使用 lxml 解析内容并提取任务数据。
  • main():协调刮擦过程,获取数据并将其保存到 CSV 文件。

import pandas as pd

async def scrape_indeed_jobs(url):
    # 步骤 1:使用 Playwright 获取页面内容
    content = await get_page_content(url)
    
    # 第 2 步:解析 HTML 并提取任务详细信息
    jobs_data = parse_job_listings(content)
    
    return jobs_data

# 要搜索的 URL
url = 'https://www.indeed.com/q-usa-jobs.html'

# 抓取和保存数据
async def main():
    # 从指定的 URL 抓取工作数据
    jobs = await scrape_indeed_jobs(url)
    
    # 第 3 步:使用 pandas 将数据保存为 CSV
    df = pd.DataFrame(jobs)
    df.to_csv('indeed_jobs.csv', index=False)
    
    print("Data saved to indeed_jobs.csv")

# 运行主函数
asyncio.run(main())

步骤 3:添加分页支持

Indeed 对其职位列表进行了分页处理,您可以轻松扩展 scraper 以处理多个页面。页面 URL 通过查询参数 start 进行调整,每新增一个页面,查询参数 start 将递增 10。

为了增强您的 scraper 从多个页面收集数据的功能,您可以实现一个名为 scrape_multiple_pages 的函数。该函数将通过逐步调整起始参数来修改基础 URL,从而访问后续页面。通过系统地逐步浏览每个页面,您可以扩大收集数据(如空缺职位)的范围和数量,从而确保获得更全面的数据集。


async def scrape_multiple_pages(base_url, pages=3):
    all_jobs = []
    
    for page_num in range(pages):
        # 更新分页 URL
        url = f"{base_url}&start={page_num * 10}"
        print(f"Scraping page: {url}")
        
        # 从每个页面抓取工作数据
        jobs = await scrape_indeed_jobs(url)
        all_jobs.extend(jobs)
    
    # 将所有工作保存到 CSV
    df = pd.DataFrame(all_jobs)
    df.to_csv('indeed_jobs_all_pages.csv', index=False)
    print("Data saved to indeed_jobs_all_pages.csv")

# 抓取多页招聘信息
asyncio.run(scrape_multiple_pages('https://www.indeed.com/jobs?q=usa', pages=3))

步骤 4:自定义职位搜索查询

要在搜刮工作中瞄准特定的职位名称或关键词,您需要在 Indeed 使用的 URL 中配置查询搜索参数。这种自定义功能允许搜索器收集特定职位或行业的数据。例如,如果您要在 http://www.indeed.com 上搜索 Python 开发人员职位,您可以将查询参数调整为包含 "Python+developer "或相关关键词。


query = "python+developer"
base_url = f"https://www.indeed.com/jobs?q={query}"
asyncio.run(scrape_multiple_pages(base_url, pages=3))

根据您的数据收集需求修改该参数,您就可以将搜索重点放在特定职位上,从而提高数据收集过程的灵活性和效率。这种方法尤其适用于适应就业市场的动态需求。

完整代码


import asyncio
from playwright.async_api import async_playwright
from lxml import html
import pandas as pd

# 步骤 1:使用 Playwright 获取页面内容
async def get_page_content(url):
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False
            proxy = {
                'server': '',
                'username': '',
                'password': ''
            }
        )  # 以标题模式运行浏览器
        page = await browser.new_page()
        await page.goto(url, wait_until='networkidle')
        
        #提取页面内容
        content = await page.content()
        await browser.close()  # 使用后关闭浏览器
        return content

# 第 2 步:使用 lxml 解析 HTML 内容
def parse_job_listings(content):
    # 使用 lxml 解析 HTML
    parser = html.fromstring(content)
    
    # 使用 XPath 选择单个职位发布
    job_posting = parser.xpath('//ul[@class="css-zu9cdh eu4oa1w0"]/li')
    
    # 提取工作数据
    jobs_data = []
    for element in job_posting[:-1]:
        title = ''.join(element.xpath('.//h2/a/span/@title'))
        if title:
            link = ''.join(element.xpath('.//h2/a/@href'))
            location = ''.join(element.xpath('.//div[@data-testid="text-location"]/text()'))
            description = ', '.join(element.xpath('.//div[@class="css-9446fg eu4oa1w0"]/ul//li/text()'))
            company_name = ''.join(element.xpath('.//span[@data-testid="company-name"]/text()'))

            # 将提取的数据添加到 jobs_data 列表中
            jobs_data.append({
                'Title': title,
                'Link': f"https://www.indeed.com{link}",
                'Location': location,
                'Description': description,
                'Company': company_name
            })
    
    return jobs_data

# 第 3 步:为单个页面抓取 Indeed 招聘信息
async def scrape_indeed_jobs(url):
    # 使用 Playwright 获取页面内容
    content = await get_page_content(url)
    
    # 解析 HTML 并提取工作数据
    jobs_data = parse_job_listings(content)
    
    return jobs_data

# 第 4 步:处理分页和搜索多个页面
async def scrape_multiple_pages(base_url, query, pages=3):
    all_jobs = []
    
    for page_num in range(pages):
        # 更新 URL 以处理分页,并添加搜索查询
        url = f"{base_url}?q={query}&start={page_num * 10}"
        print(f"Scraping page: {url}")
        
        # 抓取当前页面的工作
        jobs = await scrape_indeed_jobs(url)
        all_jobs.extend(jobs)
    
    # 将所有工作保存到 CSV 文件
    df = pd.DataFrame(all_jobs)
    df.to_csv(f'indeed_jobs_{query}.csv', index=False)
    print(f"Data saved to indeed_jobs_{query}.csv")

# 使用动态查询输入运行刮板的功能
async def run_scraper():
    # 步骤 5:要求用户输入查询和要搜索的页面数
    query = input("Enter the job title or keywords to search (e.g., python+developer): ")
    pages = int(input("Enter the number of pages to scrape: "))
    
    # 根据查询跨多个页面抓取作业
    base_url = 'https://www.indeed.com/jobs'
    await scrape_multiple_pages(base_url, query, pages)

# 运行刮刀
asyncio.run(run_scraper())

为确保刮奖过程顺利进行并降低出现拦截和验证码的风险,选择正确的代理服务器至关重要。互联网服务提供商(ISP)代理服务器是最理想的搜刮选择,它速度快、连接稳定、信任度高,因此很少被平台屏蔽。这类代理是静态的,因此要进行大规模搜索,就必须创建一个 ISP 代理池,并配置 IP 轮换以定期更换。另一种选择是住宅代理,它是动态的,与其他类型的代理服务器相比,具有最广泛的地理覆盖范围。

评论:

0 评论