Cách tạo trình thu thập dữ liệu web cơ bản với Scrapy

Các chương trình đọc thông tin từ trang web hoặc trình thu thập dữ liệu web, có tất cả những kiểu ứng dụng hữu ích.

Viết các chương trình thu thập dữ liệu web này dễ dàng hơn bạn nghĩ. Python có một thư viện tuyệt vời để viết các script trích xuất thông tin từ những trang web. Hãy cùng xem cách tạo trình thu thập dữ liệu web bằng Scrapy qua bài viết sau đây!

Cài đặt Scrapy

Scrapy là một thư viện Python được tạo ra để quét và xây dựng các trình thu thập dữ liệu web. Nó nhanh chóng, đơn giản và có thể điều hướng qua nhiều trang web mà không mất nhiều công sức.

Scrapy có sẵn thông qua thư viện Pip Installs Python (PIP). Để biết cách cài đặt PIP, vui lòng tham khảo bài viết: Cài đặt Python Package với PIP trên Windows, Mac và Linux.

Sử dụng môi trường ảo Python được ưu tiên vì nó sẽ cho phép bạn cài đặt Scrapy trong một thư mục ảo và giữ nguyên các file hệ thống. Tài liệu về Scrapy khuyên bạn nên làm điều này để có kết quả tốt nhất.

Tạo thư mục và khởi tạo một môi trường ảo.

mkdir crawler
cd crawler
virtualenv venv
. venv/bin/activate

Bây giờ, bạn có thể cài đặt Scrapy vào thư mục đó bằng lệnh PIP.

pip install scrapy

Kiểm tra nhanh để đảm bảo Scrapy được cài đặt đúng cách:

scrapy
# prints
Scrapy 1.4.0 - no active project

Usage:
scrapy <command> [options] [args]

Available commands:
bench Run quick benchmark test
fetch Fetch a URL using the Scrapy downloader
genspider Generate new spider using pre-defined templates
runspider Run a self-contained spider (without creating a project)
...
Scrapy
Scrapy

Cách tạo trình thu thập dữ liệu trên web

Bây giờ, môi trường đã sẵn sàng, bạn có thể bắt đầu tạo trình thu thập dữ liệu web. Hãy khai thác một số thông tin từ trang Wikipedia về pin:

https://en.wikipedia.org/wiki/Battery_(electricity)

Bước đầu tiên để viết trình thu thập thông tin là xác định lớp Python mở rộng từ Scrapy.Spider. Điều này cho phép bạn truy cập vào tất cả các chức năng trong Scrapy. Hãy gọi lớp này là spider1.

Lớp spider cần một vài thông tin:

  • Tên (name) để xác định spider
  • Biến start_urls chứa danh sách các URL cần thu thập dữ liệu (URL Wikipedia sẽ là ví dụ trong hướng dẫn này)
  • Phương thức parse() được sử dụng để xử lý trang web và trích xuất thông tin.
import scrapy

class spider1(scrapy.Spider):
name = 'Wikipedia'
start_urls = ['https://en.wikipedia.org/wiki/Battery_(electricity)']

def parse(self, response):
pass

Một thử nghiệm nhanh để đảm bảo mọi thứ đang chạy đúng.

scrapy runspider spider1.py
# prints
2017-11-23 09:09:21 [scrapy.utils.log] INFO: Scrapy 1.4.0 started (bot: scrapybot)
2017-11-23 09:09:21 [scrapy.utils.log] INFO: Overridden settings: {'SPIDER_LOADER_WARN_ONLY': True}
2017-11-23 09:09:21 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.memusage.MemoryUsage',
'scrapy.extensions.logstats.LogStats',

Tắt tính năng ghi nhật ký

Chạy Scrapy với lớp này xuất thông tin nhật ký sẽ không giúp ích gì ở thời điểm hiện tại. Hãy làm cho mọi thứ đơn giản bằng cách loại bỏ thông tin nhật ký dư thừa này. Thêm code sau vào đầu file.

import logging
logging.getLogger('scrapy').setLevel(logging.WARNING)

Bây giờ khi bạn chạy lại script, thông tin nhật ký sẽ không được in.

Sử dụng Chrome Inspector

Mọi thứ trên một trang web được lưu trữ trong các yếu tố HTML. Các phần tử được sắp xếp trong Document Object Model (DOM). Việc hiểu rõ về DOM rất quan trọng để tận dụng tối đa trình thu thập dữ liệu web. Trình thu thập dữ liệu web tìm kiếm thông qua tất cả các thành phần HTML trên một trang để tìm thông tin, vì vậy việc hiểu cách chúng được sắp xếp là rất quan trọng.

Google Chrome có những công cụ giúp bạn tìm các phần tử HTML nhanh hơn. Bạn có thể định vị vị trí trong HTML cho bất kỳ yếu tố nào mình thấy trên trang web bằng inspector (trình kiểm tra).

  • Điều hướng đến một trang trong Chrome
  • Đặt chuột vào phần tử bạn muốn xem
  • Nhấp chuột phải và chọn Inspect từ menu

Các bước này sẽ mở bảng điều khiển dành cho nhà phát triển với tab Elements được chọn. Ở dưới cùng của giao diện điều khiển, bạn sẽ thấy một sơ đồ cây chứa các yếu tố. Sơ đồ cây này là cách bạn sẽ nhận được thông tin cho script.

Trích xuất tiêu đề

Hãy sử dụng script để thực hiện một số công việc. Thu thập thông tin đơn giản để có được văn bản tiêu đề của trang web.

Bắt đầu script bằng cách thêm một số code vào phương thức parse() để trích xuất tiêu đề.

...
def parse(self, response):
print response.css('h1#firstHeading::text').extract()
...

Đối số response hỗ trợ một phương thức gọi là CSS() chọn các thành phần từ trang bằng cách sử dụng vị trí bạn cung cấp.

Trong ví dụ này, phần tử là h1.firstHeading. Thêm ::text vào script là những gì cần để cung cấp cho bạn nội dung của phần tử. Cuối cùng, phương thức extract() trả về phần tử đã chọn.

Chạy script này trong Scrapy để xuất tiêu đề ở dạng văn bản.

[u'Battery (electricity)']

Tìm mô tả

Bây giờ, ta đã khai thác được phần văn bản tiêu đề. Hãy làm nhiều thứ hơn với script. Trình thu thập thông tin sẽ tìm đoạn đầu tiên sau tiêu đề và trích xuất thông tin này.

Đây là sơ đồ cây yếu tố trong Chrome Developer Console:

div#mw-content-text>div>p

Mũi tên phải (>) biểu thị mối quan hệ cha - con giữa các yếu tố. Vị trí này sẽ trả về tất cả các phần tử p phù hợp, bao gồm toàn bộ mô tả. Để có được phần tử p đầu tiên, bạn có thể viết code này:

response.css('div#mw-content-text>div>p')[0]

Giống như tiêu đề, bạn thêm ::text để lấy nội dung văn bản của phần tử.

response.css('div#mw-content-text>div>p')[0].css('::text')

Biểu thức cuối cùng sử dụng extract() để trả về danh sách. Bạn có thể sử dụng hàm Python join() để ghép nối danh sách sau khi quá trình thu thập thông tin hoàn tất.

def parse(self, response):
print ''.join(response.css('div#mw-content-text>div>p')[0].css('::text').extract())

Kết quả là đoạn đầu tiên của phần văn bản!

An electric battery is a device consisting of one or more electrochemical cells with external connections provided to power electrical devices such as flashlights, smartphones, and electric cars.[1] When a battery is supplying electric power, its positive terminal is
...

Thu thập dữ liệu JSON

Scrapy có thể trích xuất thông tin dưới dạng văn bản, rất hữu ích. Scrapy cũng cho phép bạn xem dữ liệu JavaScript Object Notation (JSON). JSON là một cách gọn gàng để sắp xếp thông tin và được sử dụng rộng rãi trong phát triển web. JSON cũng hoạt động khá độc đáo với Python.

Khi cần thu thập dữ liệu dưới dạng JSON, bạn có thể sử dụng câu lệnh yield được tích hợp trong Scrapy.

Ở đây, một phiên bản mới của script sử dụng câu lệnh yield. Thay vì lấy phần tử p đầu tiên ở định dạng văn bản, câu lệnh này sẽ lấy ra tất cả các phần tử p và sắp xếp nó theo định dạng JSON.

...
def parse(self, response):
for e in response.css('div#mw-content-text>div>p'):
yield { 'para' : ''.join(e.css('::text').extract()).strip() }
...

Bây giờ, bạn có thể chạy spider bằng cách chỉ định file JSON đầu ra:

scrapy runspider spider3.py -o joe.json

Script bây giờ sẽ xuất ra tất cả các phần tử p.

[
{"para": "An electric battery is a device consisting of one or more electrochemical cells with external connections provided to power electrical devices such as flashlights, smartphones, and electric cars.[1] When a battery is supplying electric power, its positive terminal is the cathode and its negative terminal is the anode.[2] The terminal marked negative is the source of electrons that when connected to an external circuit will flow and deliver energy to an external device. When a battery is connected to an external circuit, electrolytes are able to move as ions within, allowing the chemical reactions to be completed at the separate terminals and so deliver energy to the external circuit. It is the movement of those ions within the battery which allows current to flow out of the battery to perform work.[3] Historically the term \"battery\" specifically referred to a device composed of multiple cells, however the usage has evolved additionally to include devices composed of a single cell.[4]"},
{"para": "Primary (single-use or \"disposable\") batteries are used once and discarded; the electrode materials are irreversibly changed during discharge. Common examples are the alkaline battery used for flashlights and a multitude of portable electronic devices. Secondary (rechargeable) batteries can be discharged and recharged multiple
...

Khai thác nhiều yếu tố

Cho đến nay, trình thu thập dữ liệu web đã khai thác được tiêu đề và một loại yếu tố từ trang. Scrapy cũng có thể trích xuất thông tin từ các loại yếu tố khác nhau trong một script.

Hãy để trích xuất các bản hit IMDb Box Office hàng đầu cho ngày cuối tuần. Thông tin này được lấy từ http://www.imdb.com/chart/boxoffice, trong một bảng có các hàng cho mỗi số liệu.

Phương thức parse() có thể trích xuất nhiều trường từ hàng. Sử dụng Chrome Developer Tools, bạn có thể tìm thấy các yếu tố được lồng trong bảng.

...
def parse(self, response):
for e in response.css('div#boxoffice>table>tbody>tr'):
yield {
'title': ''.join(e.css('td.titleColumn>a::text').extract()).strip(),
'weekend': ''.join(e.css('td.ratingColumn')[0].css('::text').extract()).strip(),
'gross': ''.join(e.css('td.ratingColumn')[1].css('span.secondaryInfo::text').extract()).strip(),
'weeks': ''.join(e.css('td.weeksColumn::text').extract()).strip(),
'image': e.css('td.posterColumn img::attr(src)').extract_first(),
}
...

Selector image xác định rằng img là descendant (hậu duệ) của td.posterColumn. Để trích xuất thuộc tính đúng, sử dụng biểu thức ::attr(src).

Chạy spider trả về JSON:

[
{"gross": "$93.8M", "weeks": "1", "weekend": "$93.8M", "image": "https://images-na.ssl-images-amazon.com/images/M/MV5BYWVhZjZkYTItOGIwYS00NmRkLWJlYjctMWM0ZjFmMDU4ZjEzXkEyXkFqcGdeQXVyMTMxODk2OTU@._V1_UY67_CR0,0,45,67_AL_.jpg", "title": "Justice League"},
{"gross": "$27.5M", "weeks": "1", "weekend": "$27.5M", "image": "https://images-na.ssl-images-amazon.com/images/M/MV5BYjFhOWY0OTgtNDkzMC00YWJkLTk1NGEtYWUxNjhmMmQ5ZjYyXkEyXkFqcGdeQXVyMjMxOTE0ODA@._V1_UX45_CR0,0,45,67_AL_.jpg", "title": "Wonder"},
{"gross": "$247.3M", "weeks": "3", "weekend": "$21.7M", "image": "https://images-na.ssl-images-amazon.com/images/M/MV5BMjMyNDkzMzI1OF5BMl5BanBnXkFtZTgwODcxODg5MjI@._V1_UY67_CR0,0,45,67_AL_.jpg", "title": "Thor: Ragnarok"},
...
]
Thứ Bảy, 15/02/2020 08:08
52 👨 4.973
0 Bình luận
Sắp xếp theo