Thu thập dữ liệu từ Lagou bằng Scrapy

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()

Thẻ: scrapy web-scraping python

Đăng vào ngày 19 tháng 6 lúc 16:36