Quy trình thu thập dữ liệu từ trang web Lagou không quá phức tạp, nhưng có một số vấn đề nhỏ cần chú ý. Dưới đây là hướng dẫn chi tiết.
>> scrapy startproject lagou
>> cd lagou
>> scrapy genspider job_lagou www.lagou.com
Khởi tạo cấu trúc dữ liệu
Trong file items.py, chúng ta định nghĩa các trường dữ liệu cần thu thập:
# -*- coding: utf-8 -*-
import scrapy
class JobItem(scrapy.Item):
title = scrapy.Field()
description = scrapy.Field()
url = scrapy.Field()
Tạo logic cho Spider
# -*- coding: utf-8 -*-
import re
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from scrapy.selector import Selector
from bs4 import BeautifulSoup
from lagou.items import JobItem
class LagouJobSpider(CrawlSpider):
name = "job_lagou"
allowed_domains = ["lagou.com"]
start_urls = ['http://www.lagou.com/jobs/']
rules = (
Rule(LinkExtractor(allow=r'jobs/\d+\.html'), callback='parse_job', follow=True),
)
def parse_job(self, response):
item = JobItem()
selector = Selector(response)
try:
item['title'] = selector.xpath("//title/text()").get().split('-')[0].strip()
desc_html = selector.xpath('//*[@id="container"]/div[1]/div[1]/dl[1]/dd[2]').get()
soup = BeautifulSoup(desc_html, 'html.parser')
item['description'] = self.extract_requirements(soup.get_text())
item['url'] = response.url
except Exception as e:
print(e)
return item
def extract_requirements(self, text):
pattern = re.compile(u'(Yêu cầu|Điều kiện)[::;\r\n]?')
if not pattern.search(text):
return ""
pos = pattern.search(text).span()[1]
lines = re.split(u'[;;。\r\n]', text[pos:])
filtered_lines = [line for line in lines if len(line) >= 5 and re.search(u'(Có kinh nghiệm|Thành thạo)', line)]
return "\n".join(filtered_lines)
Lưu kết quả dưới dạng JSON
# -*- coding: utf-8 -*-
import json
import codecs
class JsonPipeline(object):
def __init__(self):
self.file = codecs.open('jobs.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
line = json.dumps(dict(item), ensure_ascii=False) + '\n'
self.file.write(line)
return item
def close_spider(self, spider):
self.file.close()
Cấu hình Pipeline trong settings.py
BOT_NAME = 'lagou'
SPIDER_MODULES = ['lagou.spiders']
NEWSPIDER_MODULE = 'lagou.spiders'
ITEM_PIPELINES = {
'lagou.pipelines.JsonPipeline': 300,
}
Chạy chương trình
>> scrapy crawl job_lagou -o jobs.json -t json
Ví dụ kết quả:
{"url": "http://www.lagou.com/jobs/1102051.html", "description": "1、Có ít nhất 2 năm kinh nghiệm về thiết kế sản phẩm internet...\n2、Hiểu rõ về quy trình phát triển sản phẩm...", "title": "Product Manager"}
{"url": "http://www.lagou.com/jobs/917776.html", "description": "1、Phải có tối thiểu 2 năm kinh nghiệm trong việc lên kế hoạch và thiết kế sản phẩm...\n2、Có khả năng phân tích dữ liệu để hiểu nhu cầu người dùng...", "title": "Product Manager"}
Chương trình tiền xử lý (preprocess.py)
#!/usr/bin/env python
# coding=utf-8
import json
import re
from collections import defaultdict
def clean_data(input_file='./jobs.json'):
filter_pattern = re.compile(u'(Lương|Phúc lợi)')
results = defaultdict(str)
for line in open(input_file, 'r'):
data = json.loads(line)
if not data["description"] or filter_pattern.search(data["description"]):
continue
cleaned_desc = re.sub(r'\s+', ' ', data["description"])
if data["title"] not in results:
results[data["title"]] = cleaned_desc
else:
results[data["title"]] += f"\n{cleaned_desc}"
with open('./jobs_cleaned.json', 'w') as f:
json.dump(results, f, ensure_ascii=False)
if __name__ == "__main__":
clean_data()