项目概述

该项目是一个异步网页爬虫,旨在从给定的URL中抓取网页内容,并利用OpenAI模型从抓取到的Markdown内容中提取特定年份的年度报告PDF链接,然后下载这些PDF文件。

技术栈

  • Python: 主要开发语言。

  • asyncio: 用于异步操作,提高爬取和下载效率。

  • crawl4ai: 用于网页内容爬取。

  • openai: 用于与OpenAI API交互,进行内容分析和PDF链接提取。

  • aiohttp: 用于异步HTTP请求,实现PDF文件的下载。

  • aiofiles: 用于异步文件操作,提高文件写入效率。

  • dotenv: 用于加载环境变量。

  • logger: 用于日志记录。

核心功能

1. 网页爬取 (main 函数)

  • 使用 crawl4ai 库的 AsyncWebCrawler 进行异步网页爬取。

  • 配置浏览器为无头模式 (headless=True)。

  • 支持批量URL爬取 (arun_many)。

  • 成功爬取后,对每个网页内容进行后续处理。

2. PDF链接提取 (extract_pdfs_url 函数)

  • 利用OpenAI模型: 将网页的Markdown内容作为输入,调用OpenAI的聊天完成API。

  • 详细的提示词工程: 提示词(prompt)定义了严格的规则来识别和提取年度报告PDF链接,包括:

    • 核心筛选标准: 仅提取明确标识的PDF链接,且文件扩展名必须是 .pdf

    • 年份精准匹配: 必须包含目标年份(通过命令行参数 sys.argv[1] 传入,例如2024)的标识,支持数字、中文和英文格式。

    • 链接处理: 保留原始URL格式,仅返回纯URL,多个有效链接用英文逗号分隔,无匹配时返回空字符串。

  • 错误处理: 捕获OpenAI API调用过程中可能发生的异常。

3. PDF文件下载 (download_pdfdownload_url 函数)

  • download_url:

    • 异步下载单个URL指向的文件。

    • 设置 User-Agent 请求头以模拟浏览器访问。

    • 设置30秒的下载超时。

    • 检查HTTP响应状态码,非200则记录错误。

    • 从URL中提取文件名,并构建本地保存路径。

    • 使用 aiofiles 异步写入文件内容。

    • 记录下载成功或失败的信息。

  • download_pdf:

    • 接收从 extract_pdfs_url 获取的PDF链接列表和原始网页的 base_url

    • base_url 中解析出域名,并以此域名创建本地存储文件夹(例如 D:/Work/craw4ai_project/data/new/{domain})。

    • 使用 asyncio.gather 并发执行所有PDF下载任务,提高下载效率。

运行流程

  1. 加载环境变量: 从 .env 文件中加载 OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL 等配置。

  2. 定义目标URL: urls 列表中包含待爬取的网页URL。

  3. 执行主函数: main 函数启动爬取流程。

  4. 爬取网页: AsyncWebCrawler 访问每个URL并获取其Markdown内容。

  5. 提取PDF链接: 对于每个爬取成功的网页,调用 extract_pdfs_url,将Markdown内容和目标年份(通过命令行参数传入)发送给OpenAI模型,获取符合条件的PDF链接。

  6. 下载PDF文件: 获取到PDF链接后,调用 download_pdf 函数,并发下载所有提取到的PDF文件到本地指定目录。

如何运行

  1. 配置环境变量: 在项目根目录下创建 .env 文件,并添加以下内容:

    OPENAI_API_KEY=你的OpenAI API Key
    OPENAI_BASE_URL=你的OpenAI API Base URL (例如:https://api.openai.com/v1)
    OPENAI_MODEL=你使用的OpenAI模型名称 (例如:gpt-4o)
  2. 安装依赖:

    pip install -r requirements.txt
    # 假设 requirements.txt 包含:
    # crawl4ai
    # openai
    # python-dotenv
    # aiohttp
    # aiofiles
  3. 运行脚本:

    python your_script_name.py 2024

    其中 your_script_name.py 是你的Python文件名,2024 是你希望提取的pdf文件年份。

注意事项

  • 确保 urls 列表中的URL是有效的。

  • OpenAI API的调用会产生费用,请注意使用量。

  • 提示词的质量直接影响PDF链接提取的准确性,如有需要可根据实际情况调整提示词内容。

  • 下载路径 D:/Work/craw4ai_project/data/new/ 是硬编码的,建议根据实际需求修改为可配置的路径。

  • 命令行参数 sys.argv[1] 用于指定目标年份,确保在运行脚本时提供该参数。

代码模块示例

为了更好地理解项目功能,以下是部分核心代码模块的示例:

main 函数 (网页爬取入口)

async def main():
    browser_config = BrowserConfig(
        headless = True
    )
​
    crawler_config = CrawlerRunConfig(
        cache_mode = CacheMode.BYPASS
    )
​
    async with AsyncWebCrawler(config = browser_config) as crawler:
        result = await crawler.arun_many(
            urls = urls,
            bypass_cache = True,
            config = crawler_config
        )
        for res in result:
            if res.success:
                logger.debug(f"成功爬取:{res.url}")
                pdfs_url = await extract_pdfs_url(res.markdown)
                logger.debug(res.markdown)
                await download_pdf(pdfs_url,res.url)
            else:
                logger.debug(res.url)
        print("内容下载完成")

extract_pdfs_url 函数 (PDF链接提取)

async def extract_pdfs_url(markdown):
    client = OpenAI(
        api_key = os.getenv("OPENAI_API_KEY"),
        base_url = os.getenv("OPENAI_BASE_URL")
    )
    model = os.getenv("OPENAI_MODEL")
    prompt = f""" 
你是一个专业的网页内容分析助手。你的任务是从给定的网页Markdown内容中精准提取目标年份的年度报告PDF链接,请严格遵循以下规则:
...
"""
    user_prompt = f"""目标年份:{sys.argv[1]},markdown内容:{markdown}"""
    messages = [
        {"role":"system","content":prompt},
        {"role":"user","content":user_prompt}
    ]
​
    try:
        response = client.chat.completions.create(
        model=model,
        messages=messages,
        stream=False,
        temperature=0.7
        )
        response_content = response.choices[0].message.content
        logger.debug(f"大模型处理结果:{response_content}")
​
        pdfs = response_content.split(",")
        logger.debug(pdfs)
        return pdfs
​
    except Exception as e:
        logger.error(f"大模型分析失败: {e}")

download_url 函数 (单个文件下载)

async def download_url(url,folder):
    headers =  {'User-Agent':'xxxxxxxx'}  #替换为自己的用户代理
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(url, headers=headers, timeout=30) as response:
                if response.status != 200:
                    logger.error(f"下载失败:{url},-状态码:{response.status}")
​
                filename = url.split('/')[-1]
                filepath = os.path.join(folder,filename)
                content = await response.read()
                async with aiofiles.open(filepath,"ab") as f:
                    await f.write(content)
            logger.debug("下载成功:{filename}")
​
    except Exception as e:
        logger.error(f"下载出错:{e}")

download_pdf 函数 (并发PDF下载)

async def download_pdf(pdfs,base_url):
        domain = urlparse(base_url).netloc.replace("www.", "")
        folder_name = f"D:/Work/craw4ai_project/data/new/{domain}"
        os.makedirs(folder_name, exist_ok=True)
        download_tasks = [download_url(url,folder_name) for url in pdfs]
        await asyncio.gather(*download_tasks)
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐