deadseti/matros-dev

GitHub: deadseti/matros-dev

一款终端交互式网页数据提取工具,通过自动扫描页面结构让用户无需手写选择器即可完成从数据发现到抓取导出的全流程。

Stars: 0 | Forks: 0

# MATROS DEV **指向一个 URL。它会自动识别其中的内容,展示给你看,并等待你的确认。** 一个终端数据提取工作室:无需编写选择器,无需配置文件,无需浏览器 DevTools。 扫描页面,选择你需要的列,将其抓取到 SQLite 中,并导出到任何地方。 [![Python](https://img.shields.io/badge/python-3.10%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/) [![License](https://img.shields.io/badge/license-MIT-22d3ee)](LICENSE) [![Platform](https://img.shields.io/badge/windows%20%C2%B7%20macos%20%C2%B7%20linux-333)](#install) [![No API keys](https://img.shields.io/badge/works%20with-zero%20API%20keys-4ade80)](#places--maps) ![MATROS DEV main menu](https://static.pigsec.cn/wp-content/uploads/repos/cas/13/133bd4657ffba25c98f9ee2fe6b46f3ef919ad3e8f2faadaa20b5213291819ba.png)
## 为什么开发这个工具 每一个爬虫的起步都是一样的:打开 DevTools,寻找类名,编写选择器,运行它,得到一个空列表,返回,再试一次。然后网站改版了,你又得把这一切重做一遍。 Matros 颠覆了这个顺序。它首先读取页面,然后告诉*你*哪些内容是可提取的——它发现的每一个重复结构、每一列大概的含义、填充的频率,以及真实的样本值。你只需勾选想要的列。只有在这之后,才会进行批量获取。 这样做带来了两个结果: - **在消耗请求之前,你就知道自己会获取到什么。** 不会再发生爬取了 400 页结果却生成一个空 CSV 的情况。 - **选择器是经过生成和验证的,而不是盲目猜测的。** 每一个选择器都会在生成它的页面上重新运行;如果它无法定位回构建它的节点,就会被丢弃。 ## 安装 ``` git clone https://github.com/deadseti/matros-dev.git cd matros-dev pip install -r requirements.txt python matros.py ``` 需要 Python 3.10+。这就是全部的安装过程——其他所有内容都是可选的: ``` python -m playwright install chromium # JavaScript-rendered sites ``` **Windows:** 双击 `run.cmd`。它会自动寻找你的 Python(`py -3`、`python` 或常见的安装路径),将控制台强制设置为 UTF-8,安装任何缺失的依赖,并在出现问题时记录日志。 ## 关键的五分钟 ### 1. 扫描 ``` ┌───────────────────────────── RECON · READY ──────────────────────────────┐ │ url https://books.toscrape.com/ robots allowed │ │ status HTTP 200 rendering server-side HTML│ │ title All products | Books to Scrape pagination next-link via a │ │ size 51.2 KB in 143 ms links 71 internal │ │ language en stack Bootstrap │ └──────────────────────────────────────────────────────────────────────────┘ ``` 查看状态、robots.txt 结果和爬取延迟、服务端渲染对比 JavaScript、检测到的技术栈、Sitemap、Feed,以及该网站实际的分页工作方式。 ### 2. 查看可提取内容 ``` WHAT CAN BE EXTRACTED · 3 candidate collection(s) # Collection Source Items Fields Quality 1 col-xs-6 × 20 repeated block 20 7 excellent 2 microdata / Product microdata 20 5 good 3 table / specifications table 6 2 fair ``` 五个探测器凭实力竞争,胜出者就是能最准确描述数据的那个: | 探测器 | 读取内容 | |---|---| | **JSON-LD** | 网站发布的关于自身的 `schema.org` 数据——几乎总是最干净的数据源 | | **Microdata** | 内联的 `itemscope` / `itemprop` 注解 | | **重复块** | 由同一个模板渲染出的同级元素 | | **表格** | 真实的 `` 数据,表头会成为列名 | | **JSON API** | 指向一个 endpoint,它会直接读取结果数组 | 重复块是通过结构化方式发现的,无需针对每个网站设置规则。同级元素通过标签 + 稳定类名 + 子元素形状的签名进行分组,然后根据数量、文本实质、内部结构、链接/图像密度、语义类命名进行评分——最后还有 **大小一致性**,这被证明是最强的单一信号。来自同一个模板的行具有相似的文本长度;而布局表格的行(表头、间距行、其他所有内容)则没有。正是这一项检查,使得它在 Hacker News 上会选择 `tr.athing`,而不是包裹整个页面的 `
`。 ### 3. 确认列 这是整个工具的核心基础。 ``` # Field Type Fill Sample Selector 1 ◉ image_alt T text 100% A Light in the Attic img.thumbnail ::text 2 ◉ link T text 100% A Light in the ... h3 a ::text 3 ◉ url → url 100% https://books.toscrape.com/… a ::attr(href) 4 ◉ image_url ▣ image 100% https://books.toscrape.com/… img.thumbnail ::attr(src) 5 ◉ price $ price 100% £51.77 p.price_color ::text 6 ○ btn_block T text 100% Add to basket button.btn-block ::text 7 ○ availability ✓ bool 100% In stock p.availability ::text ↑↓ move · space toggle · a all · n none · i invert · r rename · t retype · p preview · enter confirm ``` 重命名列、更改其类型,或者在最终确认前按下 `p` 键,使用当前选中的内容预览真实的数据行。 ### 4. 计划 ``` ┌─────────────────────── ABOUT TO RUN ────────────────────────┐ │ collection li.col-xs-6 │ │ fields 5 selected │ │ page source sitemap │ │ pages up to 50 │ │ rendering plain HTTP │ │ detail pages approved recipe (≤200) │ │ pacing 3 req/s · 8 concurrent │ │ robots respected │ │ estimated requests ~250 │ └─────────────────────────────────────────────────────────────┘ ``` ### 5. 抓取 ``` ┌────────────────────── MATROS · live run ───────────────────────┐ │ HARVESTING https://books.toscrape.com/ │ │ pages ██████████████████████████████ 100% 4/4 0:00:00 │ │ │ │ 4 pages 80 records 0 duplicates 0 failed │ │ 9.3 pages/s 1 cached 0 retries 113.1 KB downloaded │ │ │ │ ✔ 20 rec https://books.toscrape.com/ │ │ ✔ 20 rec https://books.toscrape.com/catalogue/page-2.html │ └────────────────────────────────────────────────────────────────┘ ``` 每一页在完成后都会立即提交。Ctrl-C 可以干净地停止并保留所有内容——并且任务会变成可恢复的状态。 ## JavaScript 站点 大多数现代网站交给普通 HTTP 客户端的只是一个空壳。Matros 会注意到这一点,并自动通过真实的浏览器重新扫描: ``` › JavaScript-rendered page detected — re-scanning with a browser… ! Static HTML was empty; this report is from the rendered page. ``` Playwright 是可选的。没有它也不会出错——你会收到一条可操作的提示信息,而不是一堆报错堆栈。安装了它之后,整个运行过程会共享一个 Chromium 实例,并且图像、字体和跟踪器都会被拦截,因为它们根本无法改变 DOM。 | 设置 | 作用 | |---|---| | `auto_browser` | 当页面看起来像 JS 空壳时,使用浏览器重试——**默认开启** | | `use_browser` | 始终进行渲染,从不先尝试纯 HTTP | | `browser_scroll_times` | 跟随无限滚动,一旦页面停止增长就提前停止 | | `browser_click_load_more` | 不断点击“加载更多” / “显示更多”,直到该按钮消失 | | `browser_headless` | 关闭此选项可以观看真实的 Chromium 窗口执行工作 | 每次运行时也可以使用 `--browser` 强制开启。 ## 获取整个网站,而不仅仅是第一页 **Sitemap。** 分页只能给你几百页的內容;而 Sitemap 能给你网站愿意公开的所有内容。索引会被递归跟踪,`.xml.gz` 会被自动处理,并且你可以进行过滤: ``` python matros.py run https://shop.example.com/ --sitemap --match product -n 2000 ``` **恢复。** 每一页在完成的瞬间就会被记录状态(checkpointed),因此中断的运行只会让你损失未完成的页面,而不会丢失已完成的页面: ``` › resuming job #7 — 812 page(s) already done › 188 page(s) queued, 812 already done ``` 失败的页面会被重试;成功的页面会被跳过。只要存在未完成的任务,主菜单就会主动提供此选项。 **两级爬取。** 列表页很少包含完整的记录。选择 *跟踪详情页*,Matros 就会扫描一个真实的详情页,向你展示**它**的字段,等待你确认,然后将这些列合并到匹配的列表行中: ``` ✔ 20 rec https://books.toscrape.com/ › Following 5 detail page(s)… ✔ 1 rec …/a-light-in-the-attic_1000/index.html ``` 列表行保留其原有标识——数据丰富化(enrichment)只是更新操作,绝不会产生多余的行。 ## 类型推断 数值是根据数据本身进行分类的。列名仅用于打破平局,因为类名撒谎的频率远高于数值。 | 类型 | 处理方式 | |---|---| | `text` / `longtext` | 折叠空白字符,反转义 HTML 实体,进行 NFKC 规范化 | | `number` | `1 234,56` 和 `1,234.56` 都会变成 `1234.56` | | `price` | 提取数字金额;货币信息会显示在该字段上 | | `date` | 格式化为 ISO-8601,支持 ISO / `12.03.2024` / `12 Mar 2024` / `Mar 12, 2024` | | `email` | 进行验证,转为小写,还原 `[at]` 混淆 | | `phone` | 仅保留数字,国际号码保留前导 `+` | | `url` | 转换为绝对路径,剔除跟踪参数(`utm_*`、`fbclid` 等) | | `image` | 解析懒加载属性(`data-src`、`srcset`) | | `boolean` | `in stock` / `out of stock`、`yes` / `no`、`✓` / `✗` | | `identifier` | SKU / 代码——用于去重 | 该机制被刻意设计得很保守。`"6 hours ago"` **不是** 数字,`"1."` 也 **不是** 日期,因为一列数据如果被静默截断成了 `6`,其后果比原样保留为文本要严重得多。 ## 存储 每个工作区使用一个 SQLite 文件,开启 WAL 模式。 ``` projects ─┬─ jobs ─┬─ pages crawl checkpoint, one row per URL │ └─ records JSON documents + provenance └─ recipes approved extraction plans, re-runnable ``` 记录以 JSON 文档形式存储,因此网站更改其标记永远不会导致 schema 迁移。`UNIQUE(project_id, fingerprint)` 使得写入操作具备幂等性——重新运行任务绝不会产生重复行。 **去重机制被刻意设计得很保守。** 只有当一个字段几乎总是存在 *且* 几乎总是唯一时,它才会被用作键。否则,会对整条记录进行哈希处理,只有完全相同的重复项才会被合并。这一点很重要:早期版本曾将“填充率高”的字段作为键,并选择了在多行中重复出现的第一个标签的 URL——在被发现之前,它静默删除了 30 条真实记录中的 4 条。 ## 导出 支持 CSV · JSON · JSONL · XLSX · Markdown,可以同时导出多种格式,并共享同一个时间戳。 - **CSV** 使用带 BOM 的 UTF-8 编码,确保 Windows 上的 Excel 能正确显示西里尔字母和重音符号,并且会中和开头的 `=` `+` `-` `@` 字符,以防发生电子表格公式注入。 - **XLSX** 带有样式表头、冻结窗格、自动筛选和自适应列宽。 - 嵌套值可预测地展平:字典转换为 `parent.child`,标量列表转换为 `a | b | c`。 ## 地点与 maps **通过 Overpass 访问 OpenStreetMap** —— 无需 API key,开放数据。根据名称对区域进行地理编码,然后拉取指定半径内分类的 POI(兴趣点)。内置 25 个预设或使用原始 Overpass 标签过滤器。当某个镜像繁忙时,会自动切换到备用镜像列表。 ``` python matros.py osm "Lviv, Ukraine" --category cafe --radius 2000 -f csv ``` **Google Places API (New)** —— 获取 Maps 数据的官方途径。支持带分页的文本和附近搜索:名称、类别、地址、电话、网站、评分、评论数、价格水平、营业时间、坐标、Place ID。 ``` python matros.py places "coffee shops in Warsaw" -p warsaw -f xlsx ``` ## 弹性与容错 这是不那么光鲜的一半,但却是任务能够跑完的真正原因: - **基于主机级别的令牌桶速率限制**,在遇到 `429` 或声明的 `Crawl-delay` 时会自动收紧。 - **带有完全抖动的指数退避**,遵循 `Retry-After`。 - **有界并发**,连接池,HTTP/2。 - **带有 TTL 的磁盘响应缓存** —— 重新运行和重放探测过程零成本。 - **编码恢复** —— brotli/zstd/gzip,然后是 UTF-8 → cp1251 → latin-1 回退机制。 - **崩溃安全** —— 页面在完成时即提交;Ctrl-C 会将任务状态最终定为 `cancelled` 并保留完整的统计数据。 - 网络失败会返回一个设置了 `.error` 的 `Response`,而不是抛出异常,因此一个死掉的页面绝不会搞垮整个运行过程。 已经在多个在线网站上进行了测试——包括返回 403、500 的网站以及一个无法解析的域名——实现了零未处理异常。 ## CLI 交互式工作室的所有功能,均可编写脚本执行: ``` matros # interactive studio matros scan https://example.com/shop # recon report, then exit matros scan https://spa.example.com --browser # ...through a real browser matros scan https://example.com --json # machine-readable profile matros run https://example.com/shop -p shop -n 10 -f csv,json matros run https://spa.example.com/ --browser -p spa matros run https://example.com/ --sitemap --match product -n 500 matros run https://example.com/ --resume 7 # continue an interrupted job matros sitemap https://example.com/ --match blog -n 50 matros osm "Kraków, Poland" --category bakery --radius 2500 matros places "dentists in Kyiv" -n 60 matros jobs # what can be resumed matros projects # what you've collected matros export shop -f xlsx -o ./out matros config # resolved settings + paths ``` 退出代码具有明确的含义:`0` 成功,`1` 未找到/失败,`2` 使用错误或缺少 key,`3` 被 robots.txt 拦截。 ## 配置 所有配置都存放在 `/settings.json` 中,可以通过“设置”菜单或手动进行编辑。工作区默认为 `~/MatrosDev`,并可通过 `$MATROS_HOME` 或 `--workspace` 进行更改。 | 分组 | 键 | |---|---| | 礼貌策略 | `respect_robots`, `requests_per_second`, `concurrency`, `request_timeout`, `max_retries`, `rotate_user_agent` | | 爬取 | `max_pages`, `max_depth`, `follow_pagination`, `http_cache`, `cache_ttl_hours` | | 浏览器 | `auto_browser`, `use_browser`, `browser_wait_ms`, `browser_scroll_times`, `browser_click_load_more`, `browser_headless` | | 侦察 | `min_repeat_items`, `min_fill_rate`, `max_field_samples` | | 导出 | `export_formats`, `csv_delimiter`, `flatten_depth` | | 集成 | `google_places_key`, `overpass_endpoint` | ## 礼貌策略与授权 默认情况下遵循 `robots.txt`。如果路径被禁止访问,探测阶段将拒绝继续,并且声明的 `Crawl-delay` 会自动覆盖你配置的速率。该检查可以在“设置”中关闭,但关闭时会打印警告——请仅针对你已获授权的目标禁用此功能。 你需要对每个目标的服务条款以及适用于你收集的数据的任何法律负责。 ## 布局 ``` matros/ ├── cli.py argument parsing, Windows console setup ├── headless.py non-interactive subcommands ├── config.py settings and paths ├── models.py FieldSpec, Recipe, Collection, SiteProfile, Record ├── net/ │ ├── client.py rate limiting, retries, caching, encoding recovery │ ├── render.py Playwright rendering, scrolling, load-more │ └── robots.py robots.txt, crawl-delay, sitemap discovery ├── recon/ │ ├── analyzer.py the scan pass → SiteProfile │ ├── structure.py repeating-block detection + field inference │ ├── structured.py JSON-LD, microdata, OpenGraph, tech fingerprinting │ └── pagination.py next-links, numbered pagers, URL templates ├── extract/ │ ├── dom.py parsing, querying, selector synthesis │ ├── normalize.py type detection and value cleaning │ └── engine.py recipe execution ├── sources/ │ ├── places.py Google Places API (New) │ ├── overpass.py OpenStreetMap / Overpass + Nominatim │ └── sitemap.py sitemap discovery, index recursion, filtering ├── storage/ │ ├── database.py SQLite schema and queries │ └── export.py CSV / JSON / JSONL / XLSX / Markdown writers ├── pipeline/ │ └── runner.py crawl orchestration, checkpointing, enrichment └── tui/ ├── theme.py palette and gradient wordmark ├── widgets.py prompts, menus, interactive picker ├── keys.py raw key input (Windows + POSIX) ├── dashboard.py live run view └── screens.py application flows ``` 约 9,400 行代码,无框架。交互式选择器在真实终端环境下使用原始键盘输入,在其他情况下则回退到编号提示,因此相同的工作流可以通过 SSH、管道或在 CI 中运行。 ## 注意事项 - **浏览器模式很慢** —— 以秒为单位计算,而不是毫秒。在开始对数百个页面进行渲染运行之前,Matros 会发出警告。 - **无限滚动** 需要浏览器模式 *并且* `browser_scroll_times > 0`。 - **受登录保护的页面** 暂不支持 —— 无法处理 session 或表单认证。 - Google Places 和 Overpass 有它们自己的配额和条款。 ## 许可证 MIT —— 详见 [LICENSE](LICENSE)。
标签:Playwright, Python, URL抓取, 数据抓取, 数据提取, 无后门, 爬虫框架, 特征检测, 逆向工具