ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Scrapy 动态内容抓取实战:定位数据源、复现请求与 Headless Browser 集成

Scrapy 动态内容抓取实战:定位数据源、复现请求与 Headless Browser 集成 Scrapy 动态内容抓取实战定位数据源、复现请求与 Headless Browser 集成【免费下载链接】scrapyScrapy, a fast high-level web crawling scraping framework for Python.项目地址: https://gitcode.com/GitHub_Trending/sc/scrapy在浏览器中能看到的网页数据用 Scrapy 下载后往往在 HTML 中消失——这正是动态加载页面的典型症状。本篇指南以 Scrapy 官方文档docs/topics/dynamic-content.rst为主体系统讲解选取动态加载内容的完整方法论优先寻找真实数据源API/JSON 接口、用scrapy fetch检查网页源码、用Request.from_curl复现浏览器请求、按响应类型选择解析策略以及必要时接入 headless browserPlaywright。读完后你将能够独立诊断为什么 Scrapy 抓不到数据并掌握从源码复现请求到底层解析的完整实战链路。一、问题的本质浏览器看到的 DOM 不等于 Scrapy 拿到的 HTML有些网页在浏览器中打开时能看到目标数据但用 Scrapy 下载后用选择器selectors却提取不到。原因是这类页面的数据并非由初始 HTML 提供而是由 JavaScript 在页面加载后通过额外请求拉取、再动态插入 DOM。Scrapy 文档给出的处理顺序是一个清晰的决策链首选找到数据源finding the data source——直接定位并提供数据的请求从源头取数若找不到数据源但浏览器 DOM 中确实能看到数据则考虑使用 headless browser渲染页面。这个先找数据源、渲染兜底的顺序在docs/topics/dynamic-content.rst中是明确的主张后文各节均围绕这条主线展开。二、Finding the data source如何定位数据的真实来源要提取目标数据必须先找到它的位置。文档按数据形态分两条路径2.1 非文本格式图片、PDF 等如果数据本身是图片、PDF 等非文本格式浏览器开发者工具的 Network 面板可以直接定位到对应的下载请求然后按复现请求一节用 Scrapy 重放它即可。2.2 文本格式可在浏览器中选中为文本如果数据能以文本形式被选中那么它要么内嵌在 JavaScript 代码里要么以文本格式从外部资源加载。此时文档建议用wgrep这类全文搜索工具在所有资源中检索特征字符串从而找到该资源对应的 URL。找到 URL 后分两种情况数据来自原始 URL 本身即初始 HTML 里就有只是不在body可见区域而在script/内或注释里需要检查网页源码见下节数据来自另一个 URL需要复现对应请求见第四节。浏览器 Network 工具的使用细节可参考仓库文档 developer-tools 中的 Network-tool 一节其中用quotes.toscrape.com/scroll页面演示了如何从只有 Loading... 的初始响应中找到真正的 JSON API 请求。三、Inspecting the source code用 scrapy fetch 检查网页源码有时你需要检查网页的原始源码而不是浏览器渲染后的 DOM来定位数据。文档给出的标准操作是使用 Scrapy 的fetch命令把Scrapy 视角下的页面内容直接落盘scrapy fetch --nolog https://example.com response.html--nolog用于关闭日志输出保证重定向的文件只包含响应正文。从源码看FetchCommand 在 help 文案中即明确提示了这一用法output to stdout. You may want to use --nolog to disable logging这与文档命令一一对应。拿到源码后若目标数据在内嵌 JavaScript 代码script/元素里跳转至本文第五节解析 JavaScript 代码若源码里根本没有目标数据先排除只是 Scrapy 拿不到的可能换用 curl 或 wget 等 HTTP 客户端下载同一页面看它们的响应里有没有数据其他客户端能拿到→ 问题出在 Scrapy 的请求特征上。修改你的scrapy.Request使其与对方客户端一致例如使用相同的 user-agent 字符串对应设置项USER_AGENT其默认值在 default_settings.py 中定义为Scrapy/版本 (https://scrapy.org)这正是很多站点识别并降级响应 Scrapy 请求的特征或相同的Request.headers其他客户端也拿不到→ 需要让请求更接近真实浏览器如携带 cookies、特定 headers进入下一节复现请求。四、Reproducing requests把浏览器的请求原样复现到 Scrapy当数据来自额外的 API 请求时标准流程是打开浏览器 Network 工具观察浏览器是如何发起该请求的再用 Scrapy 复现它。只需发起相同 method 与 URL 的scrapy.Request有时就够了有时你还必须复现请求体body、请求头headers和表单参数。下图展示了在 Network 面板中定位到真正的数据请求type 为json的quotes?page1及其返回的 JSON 对象——这就是找到数据源环节的典型成果4.1 Request.from_curl从 cURL 命令直接生成 Scrapy 请求由于所有主流浏览器都支持把 Network 面板中的请求导出为 cURL 格式Scrapy 内置了Request.from_curl类方法直接从一条 cURL 命令生成等价的scrapy.Request。该方法定义在 Request 类中签名为Request.from_curl( curl_command: str, ignore_unknown_options: bool True, **kwargs: Any, )行为要点由 scrapy/utils/curl.py 中的curl_to_request_kwargs实现可逐一印证识别的参数URL 位置参数以及-H/--header、-X/--request、-b/--cookie、-d/--data/--data-raw、-u/--user可安全忽略的参数--compressed因为HttpCompressionMiddleware默认开启、-s/--silent、-v/--verbose、-#这些出现时不会报错未知参数默认仅发出 warningignore_unknown_optionsTrue传入ignore_unknown_optionsFalse时则抛出ValueError细节还原-d指定数据但未指定-X时method 自动取POSTcurl.py L136-L141URL 缺少 scheme 时自动补http://Cookie header 会被解析进cookies字典-u基础认证会被转换为Authorization头重复的-d参数会按 curl 的语义用拼接成一个 body注意from_curl的 docstring 中特别提醒——若 Request 子类如JsonRequest或DefaultHeadersMiddleware、UserAgentMiddleware、HttpCompressionMiddleware等下载器中间件处于启用状态最终发出的请求可能被中间件再次修改复现失败时可从这个方向排查。developer-tools 文档 中给出了一个完整的from_curl实战示例针对quotes.toscrape.com/api/quotes接口还原了 User-Agent、X-Requested-With: XMLHttpRequest、Referer 等浏览器特征头可复制参考。若只想拿到等价参数字典而不构建 Request 对象可直接使用 curl_to_request_kwargs 函数。文档还提到第三方工具curl2scrapy可作为 cURL 到 Scrapy 请求的转换辅助。4.2 什么时候不该硬复现Scrapy 可以复现任何请求但当复现全部必要请求的开发成本过高、且爬取速度不是主要矛盾时文档明确建议转向 headless browser 方案见第六节。4.3 偶发失败的诊断文档给出了一个重要判据如果你有时能拿到期望响应、有时不能问题大概率不在你的请求上而在目标服务器——它可能有 bug、过载或者正在封禁banning部分请求。封禁与应对策略参见 practices 文档的 bans 一节。五、Handling different response formats按响应类型选择解析策略拿到包含目标数据的响应后解析方式取决于响应类型。文档给出了完整分派表这里完整继承并补充说明响应类型推荐解析方式HTML / XML按常规使用 选择器JSONresponse.json()直接加载若目标数据是内嵌在 JSON 值中的 HTML/XML 片段可把该片段载入Selector后照常使用选择器JavaScript / 含目标数据的script/元素见第五节解析 JavaScript 代码CSS对response.text使用正则表达式提取图片 / PDF 等基于图像的格式从response.body读取字节用 OCR 方案提取为文本如pytesseract从 PDF 读表格时tabula-py可能是更好的选择SVG / 内嵌 SVG 的 HTMLSVG 基于 XML可直接用选择器提取否则可先把 SVG 转为栅格图片再按图片方式处理JSON 场景的两种用法data response.json() # 数据内嵌于 JSON 中的 HTML 片段时 selector Selector(textdata[html])六、Parsing JavaScript code从 JS 中提取硬编码数据若目标数据硬编码在 JavaScript 里分两步先拿到 JS 代码再从中提取数据。第一步取得 JS 代码数据在独立 JS 文件中直接读response.text数据在 HTML 页面的script/元素里用选择器提取该元素的文本例如response.css(script::text)。第二步三种提取手段按数据形态选择正则 json.loads数据恰为 JSON 文本时最简单。例如 JS 中有一行var data {field: value}; pattern r\bvar\sdata\s*\s*(\{.*?\})\s*;\s*\n json_data response.css(script::text).re_first(pattern) json.loads(json_data) {field: value}chompjs把非严格的 JS 对象解析成dict。适合var data {field: value, secondField: second value};键不带引号这类浏览器常见的 JS 对象字面量 import chompjs javascript response.css(script::text).get() data chompjs.parse_js_object(javascript) data {field: value, secondField: second value}js2xml 选择器把整段 JS 转成 XML 文档再用 XPath/CSS 选择器查询。例如 import js2xml import lxml.etree from parsel import Selector javascript response.css(script::text).get() xml lxml.etree.tostring(js2xml.parse(javascript), encodingunicode) selector Selector(textxml) selector.css(var[namedata]).get() var namedataobjectproperty namefieldstringvalue/string/property/object/var三种方案的适用边界可以归纳为数据是标准 JSON 用正则数据是 JS 对象字面量用 chompjs需要按变量名/结构做复杂查询时用 js2xml。七、Using a headless browserPlaywright 集成与 asyncio 前提文档再次强调能从额外请求中直接取数时复现请求仍是首选——结构化、完整的数据量以最小的解析时间和网络传输开销换来。但在两种情况下 headless browser 是必要工具某些请求确实难以复现签名、加密参数、复杂的 JS 逻辑链或者你需要的是请求本身给不了的东西例如浏览器视角下的网页截图。headless browser 是提供自动化 API 的特殊浏览器。使用它的前提是启用 asyncio 支持Scrapy 原生支持asyncio用startproject创建的新项目默认已启用若使用CrawlerRunner/AsyncCrawlerRunner编程接口则需通过 install_reactor 手动安装AsyncioSelectorReactorTWISTED_REACTOR的默认值即twisted.internet.asyncioreactor.AsyncioSelectorReactor。asyncio 文档中还给出了一个容易被忽视的坑twisted.internet.reactor等 import 会作为副作用安装默认 reactorreactor 一旦安装无法在运行时切换——若在 Spider 模块顶层导入了这类 Twisted 符号需把它们移入函数体内再导入。7.1 官方示例PlaywrightSpiderdocs/topics/dynamic-content.rst给出了playwright-python在 Spider 内的最小可用示例import scrapy from playwright.async_api import async_playwright class PlaywrightSpider(scrapy.Spider): name playwright start_urls [data:,] # avoid using the default Scrapy downloader async def parse(self, response): async with async_playwright() as pw: browser await pw.chromium.launch() page await browser.new_page() await page.goto(https://example.org) title await page.title() return {title: title}示例中一个值得注意的细节是start_urls [data:,]它用data:URI 触发 Spider从而绕过默认下载器对真实 URL 的 HTTP 抓取data:scheme 由 DataURIDownloadHandler 直接解析为响应不产生网络请求。这样页面的全部抓取都交给 Playwright 独立完成。文档同时给出明确警告像上面这样直接调用playwright-python会绕过 Scrapy 的大部分组件下载器中间件、去重过滤器等。对于需要与 Scrapy 生态完整集成的场景官方推荐使用独立的scrapy-playwright扩展见 Scrapy 插件生态仓库文档中作为推荐方案给出。7.2 小结三条路线的取舍场景推荐路线依据数据来自可发现的 API 请求Network 面板定位 Request复现 /from_curl结构化数据、最小网络开销数据内嵌在 JS 变量中正则 / chompjs / js2xml 解析无需执行 JS请求难复现或需要截图/渲染结果headless browserplaywright asyncio reactor集成用 scrapy-playwright文档明确列出的两个适用情形八、延伸阅读仓库内相关文档dynamic-content 原文档本篇所有方法论的出处developer-toolsscrapy shell的view(response)、fetch()、Network 工具与 livedom 的完整演示asyncioasyncio reactor 配置、install_reactor与预装 reactor 冲突处理practices被请求封禁bans时的应对实践核心实现Request.from_curl、scrapy/utils/curl.py、fetch 命令、data: URI 下载处理器。【免费下载链接】scrapyScrapy, a fast high-level web crawling scraping framework for Python.项目地址: https://gitcode.com/GitHub_Trending/sc/scrapy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表