Advertisement

爬虫 - Scrapy 爬取某招聘网站

阅读量:

文章目录

    • 项目简介

    • 一、创建项目

      • 1、终端创建项目
      • 2、修改配置
    • 二、爬取列表数据

      • 1、数据分析
      • 2、模型建立
      • 3、存储为 json 数据
      • 4、存储为 mysql 数据
    • 三、爬取列表下一页及所有数据

      • 1、特征分析
      • 2、编写方法
    • 四、图片

      • 1、添加图片保存地址
      • 2、添加图片请求
      • 3、添加图片管道
    • 五、爬取详情

    • 六、添加下载中间件

      • 1、代理 USER_AGENT
      • 2、IP 池 PROXIES
    • 七、设置日志

      • 1、设置日志级别
      • 2、设置日志保存地址

项目简介

eleduck 电鸭 是一款远程工作的招聘交流网站。这里仅做学习使用。


一、创建项目

1、终端创建项目

复制代码
    $ scrapy startproject WebScrapy  # 创建项目
    $ tree
    
    $ cd WebScrapy  # 进入项目文件
    $ scrapy genspider eleduck "https://eleduck.com" # 创建爬虫
    $ tree

复制代码
    # 检查爬虫
    $ scrapy check eleduck  # 此处根据爬虫的名字来区分,而非文件名
    ----------------------------------------------------------------------
    Ran 0 contracts in 0.000s
    
    OK
    
    # 运行爬虫
    $ scrapy crawl eleduck

2、修改配置

setting.py 中:

复制代码
    # 1 修改 USER_AGENT,防止被反扒
    USER_AGENT = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50'
    
    # 2、设置不遵守 robots 协议
    ROBOTSTXT_OBEY = False

二、爬取列表数据

1、数据分析

在这里插入图片描述

列表中包含 标题,发言人 等信息,可通过 xpath 找到


2、模型建立

item.py 已自动的创建的 WebscrapyItem 类下添加字段

复制代码
    class WebscrapyItem(scrapy.Item):
    # define the fields for your item here like:
    writer = scrapy.Field()  # 作者
    title = scrapy.Field()	# 招聘标题
    detailUrl = scrapy.Field()  # 详情地址
    iconUrl = scrapy.Field()   # 头像 icon 地址

3、存储为 json 数据

pipelines.py 中添加存储方法

复制代码
    import json
    
    class WebscrapyPipeline:
    
    def __init__(self):
        self.f = open('eleduck.json','w')
    
    def process_item(self, item, spider):
    
        # print('writer : ', item['writer'])
        # print(dict(item))
        content = json.dumps(dict(item), ensure_ascii=False) + ',\n'
        # num = \
        self.f.write(content)
        # print('num : ', num)
        return item
    
    def close_spider(self, spider):
        self.f.close()

4、存储为 mysql 数据

复制代码
    import pymysql
    
    
    # 保存到 mysql
    class WebscrapyPipeline:
    
    def __init__(self):
    
        self.conn = pymysql.connect("localhost", "root", "123456Aa*", "uinfo")
    
        self.cursor = self.conn.cursor()
    
        sql = '''
            CREATE TABLE IF NOT EXISTS  ed_list(
            id INT PRIMARY KEY AUTO_INCREMENT,
        	writer VARCHAR(100),
        	title VARCHAR(100),
        	detailUrl VARCHAR(100) )
            '''
    
        ret = self.cursor.execute(sql)
        print('创建数据表 : ', ret)
    
    
    def process_item(self, item, spider):
     
        sql = """ INSERT INTO ed_list(writer, title, detailUrl)  \
                    VALUES ('%s', '%s', '%s')""" \
                    % (item['writer'], item['title'], item['detailUrl'])
    
        # print(sql)
        try:
            # 执行sql语句
            self.cursor.execute(sql)
            # 执行sql语句
            self.conn.commit()
            print('插入数据 执行成功')
        except:
            # 发生错误时回滚
            self.conn.rollback()
            print('插入数据 执行失败')
     
        return item
    
    def close_spider(self, spider):
        self.conn.close()

三、爬取列表下一页及所有数据

1、特征分析

当有下一页的时候,下一页这个 li 的 aria-disabled 属性为 false;最后一页时,这个属性为 true;

且下一级的 a 标签 href 属性有连接信息;拼接本站域名,刚好是下一页的url,如 https://eleduck.com/?page=96
在这里插入图片描述
在这里插入图片描述


2、编写方法

eleduck.py 的 parse 方法中添加如下语句,判断是否有下一页,以及下一页的 url;

并发送下一页请求进行爬取。

复制代码
        # 检查列表是否有下一页
        existNext = htmlTree.xpath('//li[@title="下一页"]/@aria-disabled')[0]  # false
        existNext_str = str(existNext)
    
        print('existNext' ,existNext)
        if existNext_str == 'false': # 有下一页,继续请求
            next_page = htmlTree.xpath('//li[@title="下一页"]/a/@href')[0]  # /?page=95
    
            print('=' * 30, '\n', next_page)
            url = urlPrefix + next_page
            yield scrapy.Request(url, callback= self.parse)  # 发送请求,并由本 parse 方法继续处理
        else:
            print('✔️' * 20,' 不存在下一页,爬取结束 ')

四、图片

1、添加图片保存地址

setting.py 中添加图片保存地址字段

复制代码
    # IMAGES_STORE = '/Users/user/Desktop/008/'  # 绝对地址
    IMAGES_STORE = 'rsc/pics/'  # 相对地址,图片将位于 位于主目录的 rsc/pics/full 中

2、添加图片请求

pipelines.py 中添加 IconPipeline 类,处理得到的 item,并发送请求

继承自 ImagesPipeline,重写 get_media_requests 处理

重写 item_completed 方法,处理图片下载完成

复制代码
    import os
    from WebScrapy.settings import IMAGES_STORE as imges_store
    from scrapy.pipelines.images import ImagesPipeline
    
    class IconPipeline(ImagesPipeline):
    
    def get_media_requests(self, item, info):
        print('\n-- IconPipeline process_item', item)
        iconUrl = item['iconUrl']
        yield scrapy.Request(iconUrl)
    
    
    # 下载完成,重命名
    def item_completed(self, results, item, info):
    
        print('\n-- item_completed, results : ', results, ' info : ', info )
        '''
    -- item_completed, results :  [(True, {'url': 'https://duckfiles.oss-cn-qingdao.aliyuncs.com/eleduck/avatar/7e70c1b0-269d-4e0b-a78e-07f762455c2a.png!64', 'path': 'full/9add2ac38247cbe97714469ab2134ac1b83e8e28.jpg', 'checksum': '41cf3ebf2bf417f5957e642176565b37', 'status': 'downloaded'})]  
    
    info :  <scrapy.pipelines.media.MediaPipeline.SpiderInfo object at 0x7fd416c4c390>  
     
        '''
    
        dict = [x for ok, x in results if ok][0]
        print('\n-- dict : ', dict)
    
        path = dict['path']
        print('-- path : ', path)
        originPath = images_store + path
        print('-- originPath : ', originPath)
    
        writer = item['writer']
        targetPath =  images_store + writer + '.jpg'
        os.rename(originPath, targetPath)
    
        return item

3、添加图片管道

将 IconPipeline 添加到 settings 的 ITEM_PIPELINES

复制代码
    ITEM_PIPELINES = {
       'WebScrapy.pipelines.WebscrapyPipeline': 300,
       'WebScrapy.pipelines.IconPipeline': 300,
    }

运行爬虫后,会得到结果:
在这里插入图片描述


五、爬取详情

在列表中可以抓取到详情的地址,即 detailUrl 保存的内容;拼接本站url地址即详情页;

所以在获取 detailUrl 的时候,直接发出请求,让另一个parse 方法来处理即可;

本次详情数据直接保存到了 html 文件中。

修改 eleduck.py 文件:

复制代码
    urlPrefix = 'https://eleduck.com'
    
    class EleduckSpider(scrapy.Spider):
    ...
    
    def parse(self, response):
        for node in list:
            ...
            detailUrl = subnode.xpath('h2/a/@href')[0]
            ...
            yield scrapy.Request(urlPrefix+detailUrl, callback=self.parseDetail) # 发送详情请求
            yield item
            
            
     
    # 处理详情
    def parseDetail(self, response):
    
        print('\n-- parseDetail : ', response.url, type(response.url))
    
        arr = str(response.url).split('/')
    
        print(arr)
        # print(arr(-1)
    
        path = 'rsc/details/' + arr[-1] + '.html'
        print('-- detailFilePath : ', path)
    
        html = response.text
    
        with open(path, 'w') as f:
            f.write(html)
            print('详情保存成功:', path)
在这里插入图片描述

六、添加下载中间件

1、代理 USER_AGENT

1)Settings.py 中添加 USER_AGENTS 键值对

复制代码
    USER_AGENTS = ['Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
               'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1',
               'User-Agent:Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11',
               'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11']

2)在 middlewares.py 中添加 下载中间件类

创建类的时候,不能确定是 下载中间件 还是 中间件,由下一步在 setting 中配置决定。

实现 下载中间件的核心方法

复制代码
    import random
    
    class DBDownloaderMiddleware:
     
    def process_request(self, request, spider):
    
        ua = random.choice(spider.settings['USER_AGENTS'])
        request.headers['User-Agent'] = ua

3)将 DBDownloaderMiddleware 添加到 settings.pyDOWNLOADER_MIDDLEWARES 键中

复制代码
    DOWNLOADER_MIDDLEWARES = {
       # 'WebScrapy.middlewares.WebscrapyDownloaderMiddleware': 543,
       'WebScrapy.middlewares.DBDownloaderMiddleware': 544
    }

4)在返回中检测 ua 是否设置成功

可以在 middlewares.py 中添加 下载中间件类,检测返回的内容

复制代码
    # 检查 ua
    class CheckRespDownloaderMiddleware:
    
    def process_response(self, request, response, spider):
        print(request.headers['User-Agent'])
        return response

同样需要添加到 settings.pyDOWNLOADER_MIDDLEWARES 键中

复制代码
    DOWNLOADER_MIDDLEWARES = {
       # 'WebScrapy.middlewares.WebscrapyDownloaderMiddleware': 543,
       'WebScrapy.middlewares.DBDownloaderMiddleware': 544,
       'WebScrapy.middlewares.CheckRespDownloaderMiddleware': 545
    }

2、IP 池 PROXIES

方法和上面雷同

1)中添加 PROXIES 键值对

复制代码
    PROXIES=[
      {'ip_port':'http://171.35.12.213:9999' },
      {'ip_port':'http://171.35.12.212:9999', "user_password":"username:password"},
      {'ip_port':'http://171.35.12.22:9999', "user_password":"root:admin123456"},
    ]

2)在上述下载中间件类 DBDownloaderMiddleware 中添加代码

代码变为

复制代码
    class DBDownloaderMiddleware:
    
    def process_request(self, request, spider):
    
        # 设置 user-agent
        ua = random.choice(spider.settings['USER_AGENTS'])
        request.headers['User-Agent'] = ua
    
        # 设置 proxy
        proxy = random.choice(spider.settings['PROXIES'])
        if proxy['user_password'] is None:
            request.meta['proxy'] = proxy['ip_port']
        else:
            upwd = base64.b64encode(proxy['user_password'])
    
            # 令牌
            request.headers['Proxy-Authorization'] = 'Basic '+ upwd
            request.meta['proxy'] = proxy['ip_port']

七、设置日志

1、设置日志级别

settings.py 中添加 LOG_LEVEL 键值对,表明日志级别

复制代码
    LOG_LEVEL = 'WARNING'

Python 的内置日志记录定义了5个不同的级别来指示给定日志消息的严重性。以下是标准的,按降序排列:

DEBUG - 调试信息

INFO - 一般信息

WARNING - 警告信息

ERROR - 一般错误

CRITICAL - 严重错误


2、设置日志保存地址

settings.py 中添加 LOG_LEVEL 键值对,表明日志记录文件的地址

复制代码
    LOG_FILE='ed.log'

全部评论 (0)

还没有任何评论哟~