本爬虫仅供学习交流,请勿将爬取数据进行非法使用。
功能简介
本爬虫可爬取各大互联网行业常用招聘网站(目前包括 拉勾网、BOSS直聘、前程无忧、猎聘网,更多可自定义),采集职位主要信息输出到 csv 文件;
爬虫和文件写入独立两个进程(其实没必要,为了练习),进程A对每个网站的爬虫启动多线程,每个爬虫以生成器方式迭代返回数据,通过队列传输给进程B进行写入。
运行环境
代码简介
首先定义了爬虫的元类和基类,元类用来自动注册爬虫类到列表,进程只要遍历这个列表就能获得所有爬虫类了
1 2 3 4 5 6 7 8 9 10
|
class SpiderMeta(type): """爬虫类的元类,注册子类到列表,爬虫类指定此元类才能加入进程"""
spiders = []
def __new__(mcs, name, bases, attrs): mcs.spiders.append(type.__new__(mcs, name, bases, attrs)) return type.__new__(mcs, name, bases, attrs)
|
因为发现这些招聘网站对访问限制很高,所以在基类里实现了一个可以保持请求间隔和带默认 headers 的请求方法,每次请求都会计算与上次请求的间隔,间隔不够就等待,这样就不用每次 requests
后边都跟着 time.sleep
了;另外还加入了随机系数来对间隔进行浮动。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
class BaseSpider(object): """爬虫类的基类,提供需要的属性和方法"""
def request(self, method='get', url=None, encoding=None, **kwargs): """ 根据爬虫类重新封装的`requests`,可保持请求间隔,并带有默认头部 :param method: 请求方法,`get`或`post`等 :param url: 请求链接 :param encoding: 指定对返回对象进行编码 :param kwargs: 其他`requests`自带的参数 :return: Response 对象 """ if not kwargs.get('headers'): kwargs['headers'] = self.headers rand_multi = random.uniform(0.8, 1.2) interval = time.time()-self._time_recode if interval < self.request_sleep: time.sleep((self.request_sleep-interval)*rand_multi) resp = getattr(requests, method)(url, **kwargs) self._time_recode = time.time() if encoding: resp.encoding = encoding return resp
|
爬虫具体代码就不说了,主要是实现一个 crawl
方法,用 yield
返回数据。
下面是进程的代码,将元类列表的爬虫类分别传入 iter_spider
中,并启动一个线程即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
class SpiderProcess(Process): """爬虫进程"""
def iter_spider(self, spider): """对爬虫类的`crawl`方法进行迭代,数据送入队列传给另一进程""" setattr(spider, 'job', self.job) setattr(spider, 'city', self.city) generator = spider.crawl() if generator: for result in spider.crawl(): self.data_queue.put(result) self.logger.debug('%s %s %50s...(省略)' % (result.get('title'), result.get('url'), result.get('description'))) self.logger.info('%s 爬虫已结束' % spider.__class__.__name__)
def run(self): """对每个爬虫类启动单独线程""" self.set_logging() spiders = [cls() for cls in SpiderMeta.spiders] spider_count = len(spiders) threads = [] for i in range(spider_count): t = Thread(target=self.iter_spider, args=(spiders[i], )) t.setDaemon(True) t.start() threads.append(t) while True: time.sleep(1)
|
写数据进程比较简单,主要是按当前时间创建 csv 然后从队列里获取数据写入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
class WriterProcess(Process): """写数据进程"""
def __init__(self, data_queue): Process.__init__(self) self.data_queue = data_queue
def run(self): """以当前时间创建 csv 文件,并从队列中获取数据写入""" csv_name = datetime.now().strftime('%Y-%m-%d %H-%M-%S') + '.csv' with open(csv_name, 'w', encoding='utf_8_sig', newline='') as f: writer = csv.writer(f) writer.writerow(['标题', '公司', '薪水', '经验', '学历', '链接', '描述']) while True: try: result = self.data_queue.get(timeout=90) if result: row = [ result.get('title'), result.get('company'), result.get('salary'), result.get('experience'), result.get('education'), result.get('url'), result.get('description') ] writer.writerow(row) except queue.Empty: f.close()
|
运行方式
方法一:使用命令行参数
$ python3 run.py -j 后端 -c 北京
方法二:直接运行,根据提示输入参数
$ python3 run.py
请输入职业:后端
请输入城市:北京
如 $ python3 run.py -j pyhton -c 上海
,结束后会生成 Excel 表格:
代码仓库
https://github.com/zkqiang/Job-Spider