Lần trước, chúng tôi đã triển khai một trình duyệt web cơ bản tải xuống các câu hỏi mới nhất từ StackOverflow và lưu trữ kết quả trong MongoDB. Trong bài viết này, chúng tôi sẽ mở rộng trình thu thập dữ liệu để nó thu thập thông tin qua các liên kết phân trang ở cuối mỗi trang và loại bỏ các câu hỏi (tiêu đề câu hỏi và URL) từ mỗi trang.
Tiền thưởng miễn phí: Nhấp vào đây để tải xuống khung dự án Python + MongoDB với mã nguồn đầy đủ hướng dẫn bạn cách truy cập MongoDB từ Python.
Cập nhật:
- 09/06/2015 - Đã cập nhật lên phiên bản mới nhất của Scrapy (v1.0.3) và PyMongo (v3.0.3) - chúc mừng!
Trước khi bạn bắt đầu bất kỳ công việc tìm kiếm nào, hãy xem lại chính sách điều khoản sử dụng của trang web và tôn trọng tệp robots.txt. Ngoài ra, hãy tuân thủ các quy tắc đạo đức bằng cách không làm ngập một trang web với nhiều yêu cầu trong một khoảng thời gian ngắn. Đối xử với bất kỳ trang web nào bạn cóp nhặt như thể đó là của riêng bạn.
Đây là tác phẩm hợp tác giữa những người tại Real Python và György - một người đam mê Python và nhà phát triển phần mềm, hiện đang làm việc tại một công ty dữ liệu lớn và đồng thời tìm kiếm một công việc mới. Bạn có thể đặt câu hỏi cho anh ấy trên twitter - @kissgyorgy.
Bắt đầu
Có hai cách khả thi để tiếp tục từ nơi chúng ta đã dừng lại.
Đầu tiên là mở rộng Spider hiện có của chúng tôi bằng cách trích xuất mọi liên kết trang tiếp theo từ phản hồi trong parse_item
phương thức với biểu thức xpath và chỉ yield
một Request
đối tượng có lệnh gọi lại đến cùng một parse_item
phương pháp. Bằng cách này, liệu pháp sẽ tự động đưa ra một yêu cầu mới tới liên kết mà chúng tôi chỉ định. Bạn có thể tìm thêm thông tin về phương pháp này trong tài liệu Trị liệu.
Tùy chọn khác, đơn giản hơn nhiều là sử dụng một loại trình thu thập dữ liệu khác - CrawlSpider
(liên kết). Đây là phiên bản mở rộng của Spider
cơ bản , được thiết kế chính xác cho trường hợp sử dụng của chúng tôi.
CrawlSpider
Chúng tôi sẽ sử dụng cùng một dự án Scrapy từ hướng dẫn cuối cùng, vì vậy hãy lấy mã từ repo nếu bạn cần.
Tạo Boilerplate
Trong thư mục "ngăn xếp", hãy bắt đầu bằng cách tạo bảng chuẩn trình thu thập thông tin từ crawl
mẫu:
$ scrapy genspider stack_crawler stackoverflow.com -t crawl
Created spider 'stack_crawler' using template 'crawl' in module:
stack.spiders.stack_crawler
Dự án Scrapy bây giờ sẽ giống như sau:
├── scrapy.cfg
└── stack
├── __init__.py
├── items.py
├── pipelines.py
├── settings.py
└── spiders
├── __init__.py
├── stack_crawler.py
└── stack_spider.py
Và stack_crawler.py tệp sẽ giống như sau:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.contrib.spiders import CrawlSpider, Rule
from stack.items import StackItem
class StackCrawlerSpider(CrawlSpider):
name = 'stack_crawler'
allowed_domains = ['stackoverflow.com']
start_urls = ['http://www.stackoverflow.com/']
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
def parse_item(self, response):
i = StackItem()
#i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
#i['name'] = response.xpath('//div[@id="name"]').extract()
#i['description'] = response.xpath('//div[@id="description"]').extract()
return i
Chúng tôi chỉ cần thực hiện một vài cập nhật cho bản soạn thảo này…
Cập nhật start_urls
danh sách
Đầu tiên, thêm trang đầu tiên của câu hỏi vào start_urls
danh sách:
start_urls = [
'http://stackoverflow.com/questions?pagesize=50&sort=newest'
]
Cập nhật rules
danh sách
Tiếp theo, chúng ta cần thông báo cho spider biết nơi nó có thể tìm thấy các liên kết trang tiếp theo bằng cách thêm một biểu thức chính quy vào rules
thuộc tính:
rules = [
Rule(LinkExtractor(allow=r'questions\?page=[0-9]&sort=newest'),
callback='parse_item', follow=True)
]
Scrapy bây giờ sẽ tự động yêu cầu các trang mới dựa trên các liên kết đó và chuyển phản hồi đến parse_item
phương pháp để trích xuất các câu hỏi và tiêu đề.
Nếu bạn chú ý kỹ, regex này giới hạn việc thu thập thông tin trong 9 trang đầu tiên vì đối với bản trình diễn này, chúng tôi không muốn loại bỏ tất cả 176,234 trang!
Cập nhật parse_item
phương pháp
Bây giờ chúng ta chỉ cần viết cách phân tích cú pháp các trang bằng xpath, điều mà chúng ta đã làm trong hướng dẫn trước - vì vậy chỉ cần sao chép nó:
def parse_item(self, response):
questions = response.xpath('//div[@class="summary"]/h3')
for question in questions:
item = StackItem()
item['url'] = question.xpath(
'a[@class="question-hyperlink"]/@href').extract()[0]
item['title'] = question.xpath(
'a[@class="question-hyperlink"]/text()').extract()[0]
yield item
Đó là đối với con nhện, nhưng không mới bắt đầu.
Thêm thời gian trễ tải xuống
Chúng tôi cần đối xử tốt với StackOverflow (và bất kỳ trang web nào, về vấn đề đó) bằng cách đặt độ trễ tải xuống trong settings.py :
DOWNLOAD_DELAY = 5
Điều này yêu cầu Scrapy phải đợi ít nhất 5 giây giữa mọi yêu cầu mới mà nó đưa ra. Về cơ bản, bạn đang tự giới hạn tỷ lệ. Nếu bạn không làm điều này, StackOverflow sẽ giới hạn xếp hạng cho bạn; và nếu bạn tiếp tục quét trang web mà không áp đặt giới hạn tỷ lệ, địa chỉ IP của bạn có thể bị cấm. Vì vậy, hãy cư xử tử tế - Hãy đối xử với bất kỳ trang web nào mà bạn cóp nhặt như thể đó là của riêng bạn.
Giờ chỉ còn một việc cần làm - lưu trữ dữ liệu.
MongoDB
Lần trước, chúng tôi chỉ tải xuống 50 câu hỏi, nhưng vì lần này chúng tôi đang lấy nhiều dữ liệu hơn, nên chúng tôi muốn tránh thêm các câu hỏi trùng lặp vào cơ sở dữ liệu. Chúng tôi có thể làm điều đó bằng cách sử dụng bản nâng cấp MongoDB, có nghĩa là chúng tôi cập nhật tiêu đề câu hỏi nếu nó đã có trong cơ sở dữ liệu và chèn theo cách khác.
Sửa đổi MongoDBPipeline
chúng tôi đã xác định trước đó:
class MongoDBPipeline(object):
def __init__(self):
connection = pymongo.MongoClient(
settings['MONGODB_SERVER'],
settings['MONGODB_PORT']
)
db = connection[settings['MONGODB_DB']]
self.collection = db[settings['MONGODB_COLLECTION']]
def process_item(self, item, spider):
for data in item:
if not data:
raise DropItem("Missing data!")
self.collection.update({'url': item['url']}, dict(item), upsert=True)
log.msg("Question added to MongoDB database!",
level=log.DEBUG, spider=spider)
return item
Để đơn giản, chúng tôi đã không tối ưu hóa truy vấn và không xử lý các chỉ mục vì đây không phải là môi trường sản xuất.
Kiểm tra
Khởi động con nhện!
$ scrapy crawl stack_crawler
Bây giờ hãy ngồi lại và xem cơ sở dữ liệu của bạn chứa đầy dữ liệu!
$ mongo
MongoDB shell version: 3.0.4
> use stackoverflow
switched to db stackoverflow
> db.questions.count()
447
>
Kết luận
Bạn có thể tải xuống toàn bộ mã nguồn từ kho Github. Bình luận bên dưới nếu có câu hỏi. Chúc mừng!
Tiền thưởng miễn phí: Nhấp vào đây để tải xuống khung dự án Python + MongoDB với mã nguồn đầy đủ hướng dẫn bạn cách truy cập MongoDB từ Python.
Tìm kiếm thêm web cạo? Hãy chắc chắn kiểm tra các khóa học Python thực. Tìm cách thuê một người quét web chuyên nghiệp? Xem GoScrape.