Scrapy Web Scraping: Xây dựng một con nhện xử lý các trang JavaScript
Lead Scraping Automation Engineer
Tóm tắt:
- Scrapy là một framework thu thập dữ liệu, không phải là một HTTP client. Nó cung cấp cho bạn một bộ lập lịch, bộ lọc trùng lặp, trình tải xuống bất đồng bộ, và một pipeline item, vì vậy một spider chỉ cần khoảng hai mươi dòng mã ngay cả khi nó đi qua một trăm trang.
- Một spider là một lớp với ba thành phần cần thiết: một
name, một URL bắt đầu, và một phương thứcparsetrả về các từ điển. Mọi thứ khác chỉ là cấu hình. - Scrapy không bao giờ thực thi JavaScript. Spider cùng một trang trả về 10 mục từ một trang được máy chủ xử lý sẽ trả về 0 mục từ trang được máy khách xử lý, vì HTML đó chứa một container rỗng và một thẻ script.
- Một middleware tải xuống giải quyết vấn đề đó mà không cần chạm vào spider. Việc xử lý trang ở phía trên và trả về cho Scrapy một
HtmlResponsethông thường đã phục hồi tất cả 10 mục trong khi phương thứcparsevẫn giữ nguyên ở dạng byte. - Việc cố định phiên bản là quan trọng hơn bình thường ở đây. Lớp TLS của Scrapy phụ thuộc vào Twisted và pyOpenSSL, và hai tổ hợp cụ thể sẽ gây lỗi cho mọi lần tải xuống HTTPS với các thông báo lỗi liên quan đến chứng chỉ thay vì phụ thuộc.
- Bắt đầu miễn phí. API Thu thập Dữ liệu Toàn cầu được sử dụng trong pivot có một gói miễn phí, vì vậy bạn có thể chạy toàn bộ so sánh trong bài viết này mà không cần gói trả phí.
Scrapy tách rời các phần của một quá trình thu thập mà bạn quan tâm khỏi những phần bạn không. Bạn viết một bộ chọn. Scrapy sẽ xử lý hàng đợi yêu cầu, đồng thời, loại bỏ trùng lặp, phát hiện mã hóa và tuần tự hóa.
Sau đó, bạn chỉ cần chỉ nó vào một trang được xây dựng bởi một framework front-end và nhận được một tệp rỗng.
Hướng dẫn này xây dựng một spider hoạt động, cố tình làm hỏng nó trên một trang được xử lý bằng JavaScript, và sửa chữa nó bằng một middleware tải xuống — phần của Scrapy cho phép bạn thay đổi cách trang được lấy mà không thay đổi cách chúng được phân tích.
Những gì Scrapy mang lại cho bạn mà một vòng lặp yêu cầu không có
Scrapy là một động cơ thu thập dữ liệu với quan điểm về cấu trúc. Một vòng lặp tự làm trên một danh sách các URL sẽ hoạt động cho đến khi bạn cần những thứ mà Scrapy đã có:
- Một bộ lập lịch với bộ lọc trùng lặp. Các yêu cầu được xếp hàng, được loại bỏ trùng lặp bằng dấu vân tay, và được xuất với đồng thời giới hạn.
- Tải xuống bất đồng bộ mà không cần cú pháp async. Scrapy chạy trên bộ phản ứng Twisted, vì vậy nhiều yêu cầu đang được xử lý trong khi phương thức
parsecủa bạn đọc như mã đồng bộ thông thường. - Các bộ chọn tích hợp sẵn.
response.css()vàresponse.xpath()được lấy từ Parsel, cùng thư viện bộ chọn đã được đề cập trong hướng dẫn bộ chọn CSS và XPath. - Xuất dữ liệu.
-O results.jsonghi JSON, JSON Lines, CSV hoặc XML mà không cần mã tuần tự hóa. - Cài đặt sự lịch sự.
ROBOTSTXT_OBEY,DOWNLOAD_DELAY, vàAUTOTHROTTLE_ENABLEDlà các cài đặt hơn là thứ bạn triển khai. Cái đầu tiên đọc tệp được mô tả trong tiêu chuẩn Giao thức Loại trừ Robots.
Chi phí là Scrapy có một hình dạng bạn phải học. Phần thưởng sẽ đến xung quanh trang thứ ba của một quá trình thu thập.
Cài đặt Scrapy
Cài đặt vào một môi trường ảo mới và cố định ngăn xếp TLS một cách rõ ràng:
bash
python3 -m venv .venv
source .venv/bin/activate
pip install "scrapy==2.17.0" "twisted==26.4.0" "pyopenssl==25.3.0"
Ba cái cố định này là cố ý. Hỗ trợ HTTPS của Scrapy được xây dựng trên Twisted, mà lại gọi đến pyOpenSSL, và hai chế độ lỗi được ghi nhận ở cuối bài viết này đều đến từ ngăn xếp đó chứ không phải từ Scrapy tự nó.
Xác nhận các phiên bản trước khi viết bất kỳ mã spider nào:
bash
python3 -c "import importlib.metadata as m; print(m.version('scrapy'), m.version('twisted'), m.version('pyopenssl'))"
Viết Spider Đầu Tiên của Bạn
Một spider Scrapy là một lớp với một tên, một danh sách các URL khởi đầu, và một phương thức parse nhận phản hồi và trả về các mục. Lưu cái này dưới dạng quotes_spider.py:
python
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = ["https://quotes.toscrape.com/"]
def parse(self, response):
for quote in response.css("div.quote"):
yield {
"text": quote.css("span.text::text").get(),
"author": quote.css("small.author::text").get(),
"tags": quote.css("div.tags a.tag::text").getall(),
}
next_page = response.css("li.next a::attr(href)").get()
if next_page:
yield response.follow(next_page, callback=self.parse)
response.css("div.quote") trả về một danh sách bộ chọn, vì vậy vòng lặp lặp qua các phần tử hơn là chuỗi. ::text là pseudo-element của Scrapy cho nút văn bản, và .get() trả về kết quả đầu tiên trong khi .getall() trả về mọi kết quả — đó là lý do tại sao tags là một danh sách và author không phải là. response.follow chấp nhận href tương đối trực tiếp, giải quyết nó dựa trên URL hiện tại, vì vậy không cần gọi urljoin.
Khối cuối cùng là phân trang. Việc trả lại một yêu cầu từ parse đưa nó trở lại vào bộ lập lịch, và chỉ định callback của nó đến parse sẽ duyệt toàn bộ danh sách. Các hình thức chung mà mẫu này bao trùm được trình bày trong hướng dẫn về phân trang trong web scraping.
Chạy Nó Không Cần Dự Án
scrapy startproject tạo ra một gói với các thiết lập, pipeline, và thư mục spiders. Bạn chưa cần bất kỳ điều đó lúc này. runspider thực thi một tệp đơn, và -s ghi đè bất kỳ thiết lập nào từ dòng lệnh:
bash
scrapy runspider quotes_spider.py -O quotes.json \
-s LOG_LEVEL=ERROR \
-s CLOSESPIDER_PAGECOUNT=3
-O cắt ngắn tệp đầu ra, trong khi -o thêm vào đó — một sự khác biệt đáng chú ý để ghi nhớ sớm. CLOSESPIDER_PAGECOUNT=3 giới hạn việc thu thập dữ liệu trong ba trang, giúp chạy thử nghiệm lịch sự và có thể lặp lại.
Lệnh đó đã viết 30 mục: mười trích dẫn mỗi trang trên ba trang, với bản ghi đầu tiên đọc Albert Einstein và các thẻ ['change', 'deep-thoughts', 'thinking', 'world'].
Hai mươi dòng mã của spider, ba trang đã được thu thập, phân trang đã được thực hiện, JSON trên đĩa. Đây là phần mà Scrapy thực sự mạnh.
Nơi Scrapy Dừng Lại: Các Trang Được Render Bằng JavaScript
Chỉ định spider giống hệt với phiên bản khách hàng của cùng một trang bằng cách thay đổi một dòng:
python
start_urls = ["https://quotes.toscrape.com/js/"]
Sau đó, chạy lại nó:
bash
scrapy runspider quotes_spider.py -O js.json -s LOG_LEVEL=ERROR -s CLOSESPIDER_PAGECOUNT=1
Kết quả là một mảng trống. Không có mục nào, không có lỗi, mã thoát 0.
Không có gì sai với bộ chọn. Trang đã trả về HTTP 200 và Scrapy đã phân tích đúng — mã nguồn mà nó nhận được đơn giản không có các phần tử div.quote. Các trích dẫn được ghi vào DOM bởi một kịch bản sau khi tải, và thực hiện kịch bản là hành vi của trình duyệt được định nghĩa bởi mô hình kịch bản của tiêu chuẩn HTML. Scrapy là một khách hàng HTTP với một bộ phân tích HTML đi kèm. Nó lấy byte; nó không chạy một động cơ JavaScript, và hướng dẫn của Scrapy về nội dung được tải động đã nói rõ điều đó.
Theo dõi số không tĩnh lặng đó. Một lần thu thập trang JavaScript trông giống hệt nhau, cả trong đầu ra và mã thoát, như một lần thu thập một trang không có gì trên đó.
Các câu trả lời thông thường gắn một trình duyệt vào: scrapy-playwright điều khiển Chromium theo yêu cầu, và Splash chạy một dịch vụ render song song với việc thu thập. Cả hai đều hoạt động, và cả hai đều có nghĩa là mỗi yêu cầu giờ đây mang theo chi phí bộ nhớ và khởi động của trình duyệt, cộng với một thời gian chạy thứ hai để triển khai.
Di chuyển việc render ra khỏi máy hoàn toàn để giữ nguyên mô hình yêu cầu của Scrapy.
Render Trên Mạch Với Middleware Downloader
Một middleware downloader nằm giữa engine của Scrapy và downloader của nó, và nó có đúng cái hook mà vấn đề này cần. Hành vi được chỉ định: khi process_request() trả về một đối tượng Request, tham chiếu middleware downloader nêu rằng "Scrapy sẽ ngừng gọi các phương thức process_request() và lập lịch lại yêu cầu đã trả về." Đối tác của nó process_response() sau đó sẽ trả về một Response lên chuỗi.
Vì vậy, middleware có thể hoán đổi từng yêu cầu outgoing thành một POST đến một điểm cuối render, sau đó bóc tách phản hồi thành một HtmlResponse thông thường mang theo URL gốc. Spider sẽ không bao giờ biết rằng điều gì đó đã xảy ra.
Lưu điều này với tên scrapeless_middleware.py:
python
import json
import os
from scrapy.http import HtmlResponse
UNLOCKER = "https://api.scrapeless.com/api/v2/unlocker/request"
class ScrapelessMiddleware:
"""Render mỗi yêu cầu trên mạch, sau đó chuyển cho Scrapy một HtmlResponse thông thường."""
def __init__(self, token, country):
self.token = token
self.country = country
@classmethod
def from_crawler(cls, crawler):
return cls(
os.environ["SCRAPELESS_API_KEY"],
crawler.settings.get("SCRAPELESS_PROXY_COUNTRY", "US"),
)
def process_request(self, request, spider):
if request.meta.get("scrapeless"):
return None
payload = {
"actor": "unlocker.webunlocker",
"input": {
"url": request.url,
"proxy_country": self.country,
"js_render": True,
},
}
return request.replace(
url=UNLOCKER,
method="POST",
body=json.dumps(payload),
headers={"Content-Type": "application/json", "x-api-token": self.token},
meta={**request.meta, "scrapeless": True, "origin_url": request.url},
dont_filter=True,
)
def process_response(self, request, response, spider):
if not request.meta.get("scrapeless"):
return response
rendered = json.loads(response.text)["data"]
return HtmlResponse(
url=request.meta["origin_url"],
body=rendered,
encoding="utf-8",
request=request,
)
Cờ scrapeless trong request.meta ngăn chặn đệ quy vô tận. Nếu không có nó, yêu cầu POST được lập lại sẽ quay lại process_request và được gói lại một lần nữa. dont_filter=True là cần thiết vì mọi yêu cầu đã được render hiện nay đều nhắm đến cùng một URL điểm cuối, và bộ lọc trùng lặp sẽ loại bỏ tất cả ngoại trừ yêu cầu đầu tiên.
origin_url là điều làm cho sự hoán đổi không bị phát hiện. HtmlResponse được xây dựng với địa chỉ thực của trang thay vì của API, vì vậy response.url là chính xác và response.follow tiếp tục giải quyết các liên kết tương đối với cơ sở đúng. Yêu cầu đến băng tần là một POST, theo quy tắc ngữ nghĩa HTTP, trong khi phản hồi mà nhện thấy là một tài liệu HTML thông thường.
Cuối cùng, khóa API được đọc từ biến môi trường SCRAPELESS_API_KEY trong from_crawler, vì vậy không có thông tin đăng nhập nào được ghi vào tệp cấu hình. Tài liệu tham khảo đầy đủ về tham số có thể được tìm thấy trong hướng dẫn API thu thập dữ liệu toàn cầu, và hành vi render đằng sau js_render được đề cập trong hướng dẫn render trang.
Kết Nối và Chạy Cùng Một Nhện Lại
Xuất khóa, sau đó kích hoạt middleware với một cài đặt. PYTHONPATH=. cho phép runspider nhập một mô-đun từ thư mục làm việc:
bash
export SCRAPELESS_API_KEY="your_api_key"
PYTHONPATH=. scrapy runspider quotes_spider.py -O js_unlocked.json \
-s LOG_LEVEL=ERROR \
-s CLOSESPIDER_PAGECOUNT=1 \
-s 'DOWNLOADER_MIDDLEWARES={"scrapeless_middleware.ScrapelessMiddleware": 543}'
Chạy đó đã sản xuất 10 mục, với bản ghi đầu tiên lại đọc là Albert Einstein và các thẻ ['change', 'deep-thoughts', 'thinking', 'world'] — những bản ghi giống hệt mà trang được render bởi máy chủ cung cấp miễn phí.
quotes_spider.py không thay đổi bất kỳ dòng nào giữa lần chạy đó và lần thất bại. Các bộ chọn, phân trang, hình dạng mục, và xuất nguồn cấp dữ liệu đều tồn tại qua một sự thay đổi hoàn toàn trong cách các trang được lấy, đây là lập luận cho việc đưa render vào một middleware thay vì trong nhện.
Bắt đầu không cần thẻ — kế hoạch miễn phí bao gồm một lần chạy kích thước này.
Chứng Minh Tất Cả Ba Trường Hợp Trong Một Kịch Bản
Ba lệnh riêng biệt dễ dàng chạy không đồng nhất. Kịch bản này chạy cả ba lần thu thập trong một quy trình duy nhất và in ra một so sánh, vì vậy yêu cầu trên có thể được kiểm tra trong một lần:
python
import json
import os
import scrapy
from scrapy import signals
from scrapy.crawler import CrawlerProcess
from scrapy.http import HtmlResponse
UNLOCKER = "https://api.scrapeless.com/api/v2/unlocker/request"
class ScrapelessMiddleware:
"""Render mỗi yêu cầu lên trên, sau đó đưa Scrapy một HtmlResponse thông thường."""
def __init__(self, token, country):
self.token = token
self.country = country
@classmethod
def from_crawler(cls, crawler):
return cls(
os.environ["SCRAPELESS_API_KEY"],
crawler.settings.get("SCRAPELESS_PROXY_COUNTRY", "US"),
)
def process_request(self, request, spider):
if request.meta.get("scrapeless"):
return None
payload = {
"actor": "unlocker.webunlocker",
"input": {"url": request.url, "proxy_country": self.country, "js_render": True},
}
return request.replace(
url=UNLOCKER,
method="POST",
body=json.dumps(payload),
headers={"Content-Type": "application/json", "x-api-token": self.token},
meta={**request.meta, "scrapeless": True, "origin_url": request.url},
dont_filter=True,
)
def process_response(self, request, response, spider):
if not request.meta.get("scrapeless"):
return response
rendered = json.loads(response.text)["data"]
return HtmlResponse(
url=request.meta["origin_url"],
body=rendered,
encoding="utf-8",
request=request,
)
class QuotesSpider(scrapy.Spider):
name = "quotes"
def __init__(self, url, **kwargs):
super().__init__(**kwargs)
```python
self.start_urls = [url]
def parse(self, response):
for quote in response.css("div.quote"):
yield {
"text": quote.css("span.text::text").get(),
"author": quote.css("small.author::text").get(),
"tags": quote.css("div.tags a.tag::text").getall(),
}
class ScrapelessQuotesSpider(QuotesSpider):
name = "quotes-scrapeless"
custom_settings = {"DOWNLOADER_MIDDLEWARES": {ScrapelessMiddleware: 543}}
def main():
import importlib.metadata as md
print(
"scrapy", md.version("scrapy"),
"| twisted", md.version("twisted"),
"| pyopenssl", md.version("pyopenssl"),
)
jobs = [
("trang tĩnh, Scrapy thông thường", QuotesSpider, "https://quotes.toscrape.com/"),
("trang javascript, Scrapy thông thường", QuotesSpider, "https://quotes.toscrape.com/js/"),
("trang javascript, middleware Scrapeless", ScrapelessQuotesSpider, "https://quotes.toscrape.com/js/"),
]
collected = {label: [] for label, _, _ in jobs}
# Scrapy giữ các trình xử lý tín hiệu theo kiểu yếu, vì vậy giữ một tham chiếu mạnh đến mỗi cái.
handlers = []
def collector(label):
def on_item(item, response, spider):
collected[label].append(item)
handlers.append(on_item)
return on_item
process = CrawlerProcess({
"LOG_LEVEL": "ERROR",
"SCRAPELESS_PROXY_COUNTRY": "US",
})
for label, spider_cls, url in jobs:
crawler = process.create_crawler(spider_cls)
crawler.signals.connect(collector(label), signal=signals.item_scraped)
process.crawl(crawler, url=url)
process.start()
for label, _, _ in jobs:
items = collected[label]
print(f"{label}: {len(items)} mục")
if items:
print(f" tác giả đầu tiên: {items[0]['author']}")
print(f" thẻ đầu tiên: {items[0]['tags']}")
print("phương thức parse dùng chung bởi cả hai spider:", ScrapelessQuotesSpider.parse is QuotesSpider.parse)
if __name__ == "__main__":
main()
Khi chạy, nó in ra:
text
scrapy 2.17.0 | twisted 26.4.0 | pyopenssl 25.3.0
trang tĩnh, Scrapy thông thường: 10 mục
tác giả đầu tiên: Albert Einstein
thẻ đầu tiên: ['change', 'deep-thoughts', 'thinking', 'world']
trang javascript, Scrapy thông thường: 0 mục
trang javascript, middleware Scrapeless: 10 mục
tác giả đầu tiên: Albert Einstein
thẻ đầu tiên: ['change', 'deep-thoughts', 'thinking', 'world']
phương thức parse dùng chung bởi cả hai spider: True
ScrapelessQuotesSpider kế thừa từ QuotesSpider và không thêm gì ngoài custom_settings, đó là lý do dòng cuối cùng là True: cả hai lần thu thập đều thực hiện cùng một đối tượng phương thức parse. Số 0 ở giữa là vấn đề JavaScript, và số 10 bên dưới là giải pháp, được đo lường so với bộ phân tích giống nhau.
custom_settings trên lớp spider giới hạn các cài đặt cho spider đó, điều này cho phép một quy trình chạy các lần thu thập với và không có middleware. Scrapy cũng giữ những người nhận tín hiệu theo tham chiếu yếu, vì vậy một đóng gói bộ thu thập tạo ra trực tiếp bị thu hồi bộ nhớ trước khi lần thu thập kết thúc và không ghi lại gì — danh sách handlers tồn tại để giữ chúng.
Khắc phục sự cố
Mọi yêu cầu HTTPS đều thất bại với 'X509' object has no attribute 'get_extension'. Twisted quá cũ cho pyOpenSSL đã cài đặt. Twisted 24.3.0, mà một số phân phối Linux vẫn cung cấp, gọi một phương thức mà các bản phát hành pyOpenSSL hiện tại không còn cung cấp. Cài đặt twisted==26.4.0 làm sạch nó. Điều này thường xảy ra nhất khi Scrapy được cài đặt vào Python hệ thống thay vì môi trường ảo.
Mọi yêu cầu HTTPS đều thất bại với xác nhận chứng chỉ thất bại. Scrapy 2.17.0 ghép nối với Twisted 26.4.0 và pyOpenSSL 26.3.0 thất bại trong việc xác nhận chứng chỉ trên các trang công cộng thông thường. Cặp pyopenssl==25.3.0 giải quyết vấn đề này, đó là lý do bước cài đặt liệt kê cả ba phiên bản.
Middleware không bao giờ chạy. runspider không thêm thư mục làm việc vào đường dẫn nhập, vì vậy lớp được đặt tên trong DOWNLOADER_MIDDLEWARES không thể được nhập. Tiền tố lệnh với PYTHONPATH=., hoặc di chuyển spider vào một dự án được tạo ra bởi scrapy startproject, nơi mà việc phân giải mô-đun được xử lý cho bạn.
Mọi yêu cầu sau yêu cầu đầu tiên bị bỏ qua. Bộ lọc trùng lặp đang xác định điểm cuối của việc kết xuất, mà là giống nhau cho mọi trang. dont_filter=True trên yêu cầu thay thế là điều ngăn điều này.
Spider phát ra mục nhưng response.follow xây dựng URL sai. HtmlResponse được tạo ra với điểm cuối API làm URL của nó thay vì địa chỉ trang gốc. Các liên kết tương đối được giải quyết theo response.url, vì vậy origin_url phải được mang qua request.meta.
Kết luận
Sự phân chia công việc của Scrapy là điều làm cho nó đáng để học. Spider sở hữu ý nghĩa của dữ liệu; bộ tải xuống sở hữu cách mà byte đến. Giữ chúng tách biệt là lý do một trang không trả về gì có thể được làm cho trả về mười bản ghi mà không cần chỉnh sửa một bộ chọn nào.
Xây dựng spider trước tiên chống lại bất kỳ điều gì mà máy chủ gửi. Khi các bộ chọn trả lại rỗng, hãy kiểm tra xem đánh dấu có bao giờ chứa chúng trước khi chuyển đến một trình duyệt không. Nếu không, việc xử lý phía trên thông qua một middleware giữ cho quá trình thu thập dữ liệu không đồng bộ, giữ cho việc triển khai vào một tiến trình Python, và để lại mã phân tích của bạn đã được thử nghiệm đúng như nó đã có.
Sẵn sàng chạy điều này chống lại các mục tiêu của riêng bạn chưa? Tạo một tài khoản Scrapeless miễn phí, xuất khóa của bạn, và thêm middleware vào một spider đã tồn tại. Xem trang sản phẩm Universal Scraping API để biết bề mặt render, và trang giá để biết giới hạn kế hoạch.
Câu hỏi thường gặp
Q: Scrapy có thể thu thập dữ liệu từ các trang web được render bằng JavaScript một cách độc lập không?
Không. Scrapy lấy HTML qua HTTP và phân tích nó, không có động cơ JavaScript trong quy trình. Một trang mà xây dựng nội dung ở phía khách hàng xuất hiện như một container rỗng cộng với một thẻ script, và các bộ chọn không khớp với gì cả. Việc render phải xảy ra ở nơi khác — trong một trình duyệt gắn liền với quá trình thu thập, hoặc phía trên trong một API render mà đầu ra của nó được nạp lại qua một middleware downloader.
Q: Sự khác biệt giữa downloader middleware và spider middleware là gì?
Một downloader middleware ngồi giữa động cơ và downloader, vì vậy nó nhìn thấy mọi yêu cầu trước khi gửi và mọi phản hồi trước khi được phân tích — là nơi thích hợp cho proxy, tiêu đề, và render. Một spider middleware ngồi giữa động cơ và spider, xử lý các mục và yêu cầu mà các callback của bạn tạo ra. Thay đổi cách một trang được lấy thuộc về một downloader middleware.
Q: Tôi có cần scrapy startproject, hay chỉ cần một tệp đơn lẻ là đủ?
Một tệp đơn lẻ chạy với scrapy runspider là đủ cho một spider, và mọi lệnh trong hướng dẫn này đều sử dụng nó. Tạo một dự án khi bạn cần các cài đặt được chia sẻ, pipeline mục, nhiều spider, hoặc triển khai — bố cục dự án cung cấp cho bạn một module cài đặt và các đường dẫn nhập mà có thể giải quyết mà không cần PYTHONPATH.
Q: Tại sao spider của tôi trả về không có mục nào mà không có thông báo lỗi?
Bởi vì một bộ chọn rỗng không phải là một lỗi trong Scrapy. Nguyên nhân phổ biến nhất là nội dung được chèn bởi JavaScript sau khi tải, một bộ chọn được viết chống lại đánh dấu mà trình duyệt's inspector hiển thị nhưng phản hồi thô không chứa, hoặc một phản hồi trả về một trang ngắt quãng với HTTP 200. In len(response.text) và tìm kiếm trong thân để tìm một chuỗi bạn mong đợi trước khi giả định bộ chọn là sai.
Q: Middleware có làm chậm quá trình thu thập không?
Mỗi yêu cầu trở thành một fetch được render thay vì một fetch thô, vì vậy độ trễ trên mỗi yêu cầu tăng lên. Mô hình đồng thời của Scrapy không thay đổi, dù vậy: các yêu cầu vẫn chạy qua cùng một bộ lập lịch và cùng một giới hạn CONCURRENT_REQUESTS, không có quy trình trình duyệt cho mỗi yêu cầu. Bật middleware chỉ cho các miền cần nó và để lại phần còn lại trên downloader thông thường.
Q: Làm thế nào để tôi giữ khóa API của mình ra ngoài codebase?
Đọc khóa từ môi trường bên trong from_crawler, như middleware ở đây làm với os.environ["SCRAPELESS_API_KEY"]. Khóa không bao giờ xuất hiện trong tệp cài đặt, vì vậy không có gì nhạy cảm bị cam kết và cùng một mã chạy trong phát triển và sản xuất chống lại các chứng chỉ khác nhau.
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.



