Trích xuất dữ liệu từ HTML trong Python với Respondo bởi Scrapeless
Expert in Web Scraping Technologies
TL;DR:
- Respondo là thư viện Python mã nguồn mở của Scrapeless để chuyển đổi HTML và JSON thành các bản ghi. Nó hoạt động trên các trang và phản hồi API mà bạn đã có, và không có phụ thuộc thời gian chạy.
- Cài đặt phiên bản 0.6 từ GitHub. Gói
respondotrên PyPI vẫn ở phiên bản 0.4.0, trước khi có các công thức trường, trợ giúp JSON Lines và chế độ nhóm. - Một công thức trường ánh xạ mỗi cột tới một bộ chọn. Các công thức có thể đọc một thuộc tính thay vì văn bản và có thể nằm trong một tệp JSON bên cạnh mã của bạn.
- Đầu ra CSV được an toàn cho bảng tính theo mặc định. Các giá trị bắt đầu như công thức sẽ có tiền tố là dấu nháy đơn, và các số vẫn giữ nguyên dạng số.
- Respondo không lấy hoặc hiển thị các trang. API Truy xuất Toàn cầu của Scrapeless thu thập HTML, và Respondo chuyển đổi nó thành các hàng.
- Hãy thử bước thu thập trên gói miễn phí của Scrapeless và trích xuất trang đầu tiên của bạn trong vài phút.
Để trích xuất dữ liệu từ HTML trong Python, bạn cần hai điều: chính HTML và mã chuyển đổi thẻ thành các trường mà bạn có thể sử dụng. Phần thứ hai thường phát triển thành các vòng lặp một lần và mã CSV khác nhau cho mỗi trang web.
Respondo đóng gói phần thứ hai đó. Bạn mô tả mỗi trường một lần, như một bộ chọn cộng với một thuộc tính tùy chọn, và Respondo trả về một danh sách các từ điển mà bạn có thể ghi vào CSV hoặc JSON Lines, từ Python hoặc từ dòng lệnh. Hướng dẫn này xây dựng một danh mục sách nhỏ từ một trang thực hành công khai, sau đó kết hợp Respondo với Scrapeless cho bước thu thập.
Điều gì là Respondo
Respondo là một bộ công cụ trích xuất cục bộ được duy trì dưới tổ chức GitHub của Scrapeless và được xuất bản theo giấy phép MIT. Kho lưu trữ nguồn trên GitHub mô tả sự tách biệt trong một dòng: thu thập với Scrapeless, sau đó trích xuất, biến đổi và xuất khẩu với Respondo.
Thư viện hoạt động trên nội dung bạn đã có. Các chức năng HTML của nó xây dựng trên module html.parser của thư viện chuẩn, vì vậy môi trường đã cài đặt không có gì ngoài chính Respondo. Phiên bản 0.6 bao gồm bốn loại công việc:
- các bản ghi lặp lại từ HTML, sử dụng các bộ chọn theo kiểu CSS và các công thức trường có thể tái sử dụng;
- truy vấn, phẳng, chiếu và kết hợp các bản vá trên tài liệu JSON;
- tóm tắt trang, nguồn cấp dữ liệu và sơ đồ trang web;
- một lệnh
respondovới 21 chế độ, bao gồm xử lý theo nhóm một thư mục toàn bộ.
Respondo không lấy URL hoặc khởi động trình duyệt, và nó không chứa khách hàng API Scrapeless. Nếu bạn vẫn đang quyết định những gì phân tích bao gồm, tổng quan về phân tích dữ liệu là gì của chúng tôi bao gồm các khái niệm.
Cài đặt Respondo 0.6
Cài đặt Respondo trực tiếp từ GitHub, gắn chặt với một cam kết. Nó cần Python 3.9 trở lên:
bash
python -m pip install "git+https://github.com/scrapeless-ai/respondo@be376d112ecf681011a079e809acae46b7e1ff59"
respondo --version
text
respondo 0.6.0
Cam kết này giữ môi trường của bạn trên chính mã mà hướng dẫn này đã được thử nghiệm. Một pip install respondo thông thường sẽ cài đặt phiên bản 0.4.0 từ PyPI, và các chức năng được sử dụng bên dưới không có trong đó. Sau khi cài đặt, pip list chỉ hiển thị respondo và pip.
Định nghĩa một Công Thức Trường
Một công thức trường là một từ điển ánh xạ mỗi cột đầu ra tới một quy tắc để tìm nó bên trong một mục lặp lại. Một chuỗi thông thường là một bộ chọn mà văn bản của nó trở thành giá trị. Một từ điển thêm các tùy chọn:
selectortìm yếu tố bên trong mục hiện tại.attrđọc một thuộc tính nhưhrefhoặctitlethay vì văn bản.required: Truephát sinh lỗi khi một mục không có kết quả phù hợp.many: Truetrả về một danh sách, vàdefaultthiết lập một giá trị thay thế cho một giá trị thiếu.
Các bộ chọn bao trùm thẻ, lớp, ID, kiểm tra thuộc tính, tổ hợp con cháu và con, và các nhóm dấu phẩy. Đó là một tập con có chủ ý của quy chuẩn về Bộ chọn W3C: các lớp giả như :nth-child và các tổ hợp anh em bị từ chối bằng ValueError thay vì bị bỏ qua.
Các công thức cũng có thể sống trong một tệp JSON, giúp giữ các bộ chọn ra khỏi mã của bạn và cho phép dòng lệnh tái sử dụng chúng. Lưu điều này dưới dạng book-fields.json:
json
{
"title": {"selector": "h3 a", "attr": "title", "required": true},
"price": ".price_color",
"availability": ".availability",
"url": {"selector": "h3 a", "attr": "href"}
}
Trường title đọc thuộc tính title của liên kết một cách có chủ đích. Văn bản liên kết hiển thị trên trang thực hành được rút ngắn cho các tên dài, trong khi thuộc tính giữ nguyên tiêu đề đầy đủ.
Trích xuất Bản Ghi và Ghi CSV
extract_records nhận HTML, một bộ chọn cho mục lặp lại và công thức, và trả về một từ điển cho mỗi mục. Ví dụ này sử dụng hai mục được sao chép từ danh mục bí ẩn của trang thực hành:
python
from respondo import extract_records, normalize_url, records_to_csv
PAGE_URL = "https://books.toscrape.com/catalogue/category/books/mystery_3/index.html"
html = """
<article class="product_pod">
<h3><a href="../../../sharp-objects_997/index.html" title="Sharp Objects">Sharp Objects</a></h3>
<p class="price_color">£47.82</p>
<p class="instock availability"><i class="icon-ok"></i> In stock</p>
</article>
<article class="product_pod">
<h3><a href="../../../in-a-dark-dark-wood_963/index.html" title="In a Dark, Dark Wood">In a Dark, Dark ...</a></h3>
<p class="price_color">£19.63</p>
<p class="instock availability"><i class="icon-ok"></i> In stock</p>
</article>
"""
books = extract_records(html, "article.product_pod", {
"title": {"selector": "h3 a", "attr": "title", "required": True},
"price": ".price_color",
"availability": ".availability",
"url": {"selector": "h3 a", "attr": "href"},
})
for book in books:
book["url"] = normalize_url(book["url"], base=PAGE_URL)
print(records_to_csv(books), end="")
text
title,price,availability,url
Sharp Objects,£47.82,In stock,https://books.toscrape.com/catalogue/sharp-objects_997/index.html
"In a Dark, Dark Wood",£19.63,In stock,https://books.toscrape.com/catalogue/in-a-dark-dark-wood_963/index.html
Ba chi tiết được xử lý cho bạn. Văn bản của .availability trở lại đã được cắt bớt, không có phần tử biểu tượng. href tương đối trở thành một URL tuyệt đối, được giải quyết dựa trên địa chỉ trang theo cách mà quy định về quy tắc giải quyết tham chiếu URI mô tả. Và tiêu đề chứa dấu phẩy sẽ được trích dẫn trong CSV.
records_to_csv cũng bảo vệ chống lại việc tiêm công thức bảng tính, một rủi ro mà mục OWASP về tiêm CSV mô tả. Một chuỗi bắt đầu bằng =, +, -, @, một dấu tab hoặc một dòng mới sẽ có dấu nháy đơn ở phía trước, vì vậy =HYPERLINK(1) được viết thành '=HYPERLINK(1), trong khi một số thực như -5 giữ nguyên như vậy. Chỉ truyền escape_formulas=False khi file không bao giờ được đưa vào bảng tính.
Đi xa hơn: Tóm tắt trang, dòng JSON và CLI
Các bản ghi là một đầu ra. Gói tương tự cũng tóm tắt toàn bộ các trang và xử lý dòng JSON, và lệnh respondo của nó chạy việc trích xuất bản ghi từ shell, cho một file hoặc toàn bộ thư mục.
Tóm tắt một toàn bộ trang
extract_page trả về tiêu đề, văn bản, siêu dữ liệu, tiêu đề, liên kết, hình ảnh và bảng của một tài liệu trong một lần gọi. Chạy nó trên một bản sao đã lưu của trang danh mục bí ẩn:
python
from respondo import extract_page
with open("mystery-page-1.html", encoding="utf-8") as handle:
page = extract_page(
handle.read(),
base="https://books.toscrape.com/catalogue/category/books/mystery_3/index.html",
)
print(sorted(page))
print(page["headings"][:2])
print(len(page["links"]), "links,", len(page["images"]), "images")
text
['headings', 'images', 'links', 'meta', 'tables', 'text', 'title']
[{'level': 1, 'id': '', 'text': 'Mystery'}, {'level': 3, 'id': '', 'text': 'Sharp Objects'}]
95 links, 20 images
Văn bản, tiêu đề và liên kết bỏ qua các script, styles và phần đầu tài liệu, và các liên kết tương đối được giải quyết dựa trên base.
Truy vấn dòng JSON
jsonl_dumps viết các bản ghi dưới dạng Dòng JSON, một đối tượng gọn gàng cho mỗi dòng, và iter_jsonl đọc chúng lại một cách lười biếng. json_query sau đó rút giá trị ra bằng một ngôn ngữ đường dẫn nhỏ mà luôn trả về một danh sách:
python
from respondo import iter_jsonl, json_query
with open("mystery-books.jsonl", encoding="utf-8") as handle:
books = list(iter_jsonl(handle))
print(len(books), "records")
print(json_query(books, "$[*].title")[:3])
print(json_query(books, "[-1].price"))
text
20 records
['Sharp Objects', 'In a Dark, Dark Wood', 'The Past Never Ends']
['£20.89']
Cú pháp đường dẫn bao gồm các khóa dấu chấm, khóa được trích dẫn, chỉ số âm và * wildcards. Nó không có bộ lọc hoặc suy giảm đệ quy, và một đường dẫn không khớp với gì thì trả về một danh sách trống.
Chạy cùng một công thức từ dòng lệnh
Lệnh respondo đọc một file cục bộ và viết JSON theo mặc định, hoặc CSV và Dòng JSON với --format:
bash
respondo records mystery-page-1.html --selector article.product_pod --fields book-fields.json --format csv | head -4
text
title,price,availability,url
Sharp Objects,£47.82,In stock,../../../sharp-objects_997/index.html
"In a Dark, Dark Wood",£19.63,In stock,../../../in-a-dark-dark-wood_963/index.html
The Past Never Ends,£56.50,In stock,../../../the-past-never-ends_942/index.html
Trong chế độ records, các URL giữ nguyên như chúng xuất hiện trên trang, ngay cả khi --base được truyền. Giải quyết chúng trong Python với normalize_url khi bạn cần các liên kết tuyệt đối.
Xử lý một thư mục các trang đã lưu
Chế độ lô chạy một công thức trên mỗi file khớp trong một thư mục, theo thứ tự tên file, và viết một hàng kết quả cho mỗi file:
bash
respondo records responses/ --batch --pattern '*.html' \
--selector article.product_pod --fields book-fields.json \
--format jsonl --output results.jsonl
python -c "import json; [print(row['source'], row['status'], len(row['result'])) for row in map(json.loads, open('results.jsonl'))]"
text
mystery-page-1.html ok 20
mystery-page-2.html ok 12
Mỗi hàng mang source, status, result và error, vì vậy một file không đọc được không dừng lại phần còn lại. Hai trang giữ tất cả 32 sách trong danh mục. Chế độ lô không bao giờ ghi đè: chạy cùng một lệnh lần nữa dừng lại với respondo: batch output exists và trạng thái thoát 1, và một đường dẫn đầu ra bên trong thư mục đầu vào bị từ chối vì không an toàn.
Nơi Respondo Dừng lại: Thu thập Trang Với Scrapeless
Respondo phân tích những gì nó được cung cấp và không hơn thế. Nó không tải xuống các trang hoặc chạy JavaScript, vì vậy các bộ chọn của nó chỉ thấy HTML mà chúng nhận được. Đối với bước đó, API Scraping Toàn cầu Scrapeless lấy một URL và trả về trang, vì vậy hai nửa vẫn tách biệt: khóa API thuộc về cuộc gọi thu thập, và việc trích xuất chạy cục bộ.
Thiết lập điều này ngay bây giờ? Kế hoạch miễn phí Scrapeless bao gồm các yêu cầu đầu tiên của bạn.
Kịch bản này thu thập trang danh mục bí ẩn sống thông qua diễn viên unlocker.webunlocker, sau đó chạy cùng một công thức trên đó. Nó đọc khóa của bạn từ biến môi trường SCRAPELESS_API_KEY:
python
import json
import os
import urllib.request
from respondo import extract_records, jsonl_dumps, normalize_url, records_to_csv
PAGE_URL = "https://books.toscrape.com/catalogue/category/books/mystery_3/index.html"
BOOK_FIELDS = {
"title": {"selector": "h3 a", "attr": "title", "required": True},
"price": ".price_color",
"availability": ".availability",
"rating": {"selector": "p.star-rating", "attr": "class"},
"url": {"selector": "h3 a", "attr": "href"},
}
def fetch_html(url):
payload = {"actor": "unlocker.webunlocker", "input": {"url": url, "method": "GET", "js_render": False}}
request = urllib.request.Request(
"https://api.scrapeless.com/api/v2/unlocker/request",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json", "x-api-token": os.environ["SCRAPELESS_API_KEY"]},
)
with urllib.request.urlopen(request, timeout=120) as response:
body = json.load(response)
if body.get("code") != 200:
raise RuntimeError(f"Scrapeless returned code {body.get('code')}")
return body["data"]
html = fetch_html(PAGE_URL)
books = extract_records(html, "article.product_pod", BOOK_FIELDS)
for book in books:
book["url"] = normalize_url(book["url"], base=PAGE_URL)
book["rating"] = book["rating"].split()[-1]
print(len(books), "books")
print(records_to_csv(books[:3]), end="")
with open("mystery-books.jsonl", "w", encoding="utf-8") as handle:
handle.write(jsonl_dumps(books))
text
20 books
title,price,availability,rating,url
Sharp Objects,£47.82,In stock,Four,https://books.toscrape.com/catalogue/sharp-objects_997/index.html
"In a Dark, Dark Wood",£19.63,In stock,One,https://books.toscrape.com/catalogue/in-a-dark-dark-wood_963/index.html
The Past Never Ends,£56.50,In stock,Four,https://books.toscrape.com/catalogue/the-past-never-ends_942/index.html
API gói trang trong một phong bì JSON, {"code": 200, "data": "<html>…"}, và urlopen nâng một HTTPError cho một lỗi HTTP trước khi phong bì được đọc. Đánh giá đến từ danh sách lớp của p.star-rating, lớp cuối cùng của nó chỉ ra số lượng sao. Hướng dẫn bắt đầu sử dụng API Scraping Toàn cầu liệt kê các tùy chọn yêu cầu khác, như nước proxy và xử lý chuyển tiếp.
Khắc phục sự cố
| Những gì bạn thấy | Nguyên nhân | Sửa |
|---|---|---|
ValueError: required field has no matches |
Một mục thiếu một trường được đánh dấu required |
Kiểm tra bộ chọn với trang, hoặc bỏ required và sử dụng default |
ValueError: unsupported selector syntax |
Bộ chọn sử dụng một lớp giả như :nth-child |
Chọn theo lớp, ID hoặc thuộc tính thay thế |
ValueError: expected a tag, class, ID or attribute selector |
Bộ chọn sử dụng + hoặc ~ |
Sử dụng tổ hợp con cháu hoặc con cái |
| Một cột trống cho mỗi hàng | Nội dung được thêm vào bởi JavaScript sau khi tải | Yêu cầu trang với js_render được bật |
| URL tương đối trong đầu ra CLI | Chế độ records giữ giá trị thuộc tính như chúng là |
Giải quyết chúng bằng normalize_url trong Python |
| Một dấu nháy đơn trước một số giá trị CSV | Thoát công thức được bật theo mặc định | Giữ lại nó, hoặc truyền escape_formulas=False cho các người tiêu dùng đáng tin cậy |
respondo: batch output exists |
Tệp đầu ra đã có sẵn | Chọn một tên tệp mới |
respondo: batch unsafe output path |
Tệp đầu ra nằm trong thư mục đầu vào | Viết kết quả ở nơi khác |
Đối với các trang xây dựng nội dung của chúng trong trình duyệt, kết xuất trang với Universal Scraping API hướng dẫn qua các tùy chọn, và tài liệu JS Render liệt kê các tham số. Kiểm tra giá cả để xem yêu cầu được kết xuất tốn bao nhiêu.
Kết luận
Respondo biến nửa quá trình trích xuất của một công việc thu thập dữ liệu thành cấu hình: một bộ chọn cho mục lặp lại và một công thức cho các trường của nó. Từ đó, extract_records trả về các từ điển mà records_to_csv hoặc jsonl_dumps chuyển đổi thành các tệp. Lệnh respondo chạy cùng một công thức trên một thư mục và báo cáo kết quả cho mỗi trang.
Giữ hai nửa tách biệt. Cài đặt 0.6 từ GitHub, thu thập các trang bằng Universal Scraping API và để Respondo làm việc với những gì trở lại, mà không có kết nối mạng hoặc thông tin xác thực riêng.
Sẵn sàng cung cấp cho Respondo các trang thực sự? Bắt đầu với gói miễn phí của Scrapeless và thu thập trang đầu tiên của bạn.
Câu hỏi thường gặp
Hỏi: Respondo là gì?
Respondo là một thư viện Python mã nguồn mở từ Scrapeless cho phép trích xuất, chuyển đổi và xuất dữ liệu từ HTML và JSON mà bạn đã có. Nó không có phụ thuộc vào thời gian chạy và hoạt động hoàn toàn trên máy của bạn.
Hỏi: Làm thế nào để tôi cài đặt Respondo 0.6?
Cài đặt nó từ GitHub với python -m pip install "git+https://github.com/scrapeless-ai/respondo@be376d112ecf681011a079e809acae46b7e1ff59". Gói PyPI ở phiên bản 0.4.0 và thiếu các tính năng trong hướng dẫn này.
Hỏi: Respondo có thể tải xuống các trang web không?
Không. Respondo chỉ phân tích nội dung mà bạn truyền cho nó. Sử dụng Universal Scraping API của Scrapeless, hoặc một nguồn HTML khác, cho bước tải xuống.
Hỏi: Làm thế nào để tôi trích xuất dữ liệu từ HTML sang CSV trong Python với Respondo?
Gọi extract_records với HTML, một bộ chọn cho mục lặp lại và một công thức trường, sau đó truyền kết quả cho records_to_csv. Từ shell, respondo records page.html --selector … --fields recipe.json --format csv làm điều tương tự.
Hỏi: Những bộ chọn CSS nào mà Respondo hỗ trợ?
Thẻ, lớp, ID, kiểm tra thuộc tính, tổ hợp con cháu và con cái, và nhóm dấu phẩy. Lớp giả và tổ hợp anh chị em gây ra một ValueError.
Hỏi: Tại sao CSV của tôi có dấu nháy đơn trước một số giá trị?
Respondo thêm tiền tố cho các chuỗi bắt đầu bằng =, +, -, @, một tab hoặc một ngắt dòng, để các bảng tính không chạy chúng dưới dạng công thức. Các số được để riêng, và escape_formulas=False tắt tiền tố.
Hỏi: Respondo có xử lý các trang được kết xuất bằng JavaScript không?
Respondo phân tích HTML mà nó nhận được và không chạy các kịch bản. Lấy những trang như vậy với việc kết xuất JavaScript được bật trong Universal Scraping API, sau đó truyền HTML đã kết xuất cho Respondo.
Tại Scrapless, chúng tôi chỉ truy cập dữ liệu có sẵn công khai trong khi tuân thủ nghiêm ngặt các luật, quy định và chính sách bảo mật trang web hiện hành. Nội dung trong blog này chỉ nhằm mục đích trình diễn và không liên quan đến bất kỳ hoạt động bất hợp pháp hoặc vi phạm nào. Chúng tôi không đảm bảo và từ chối mọi trách nhiệm đối với việc sử dụng thông tin từ blog này hoặc các liên kết của bên thứ ba. Trước khi tham gia vào bất kỳ hoạt động cạo nào, hãy tham khảo ý kiến cố vấn pháp lý của bạn và xem xét các điều khoản dịch vụ của trang web mục tiêu hoặc có được các quyền cần thiết.



