专栏名称: dwzb
目录
相关文章推荐
潇湘晨报  ·  湖南邵阳一烧烤店推出“麻辣竹签”10元1份! ... ·  22 小时前  
潇湘晨报  ·  知名巨头宣布:裁员7500人! ·  3 天前  
51好读  ›  专栏  ›  dwzb

简易多线程爬虫框架

dwzb  · 掘金  ·  · 2018-06-03 08:05

正文

简易多线程爬虫框架

本文首发于 知乎

本文使用多线程实现一个简易爬虫框架,让我们只需要关注网页的解析,不用自己设置多线程、队列等事情。调用形式类似scrapy,而诸多功能还不完善,因此称为简易爬虫框架。

这个框架实现了 Spider 类,让我们只需要写出下面代码,即可多线程运行爬虫

class DouBan(Spider):

    def __init__(self):
        super(DouBan, self).__init__()
        self.start_url = 'https://movie.douban.com/top250'
        self.filename = 'douban.json' # 覆盖默认值
        self.output_result = False 
        self.thread_num = 10

    def start_requests(self): # 覆盖默认函数
        yield (self.start_url, self.parse_first)

    def parse_first(self, url): # 只需要yield待爬url和回调函数
        r = requests.get(url)
        soup = BeautifulSoup(r.content, 'lxml')

        movies = soup.find_all('div', class_ = 'info')[:5]
        for movie in movies:
            url = movie.find('div', class_ = 'hd').a['href']
            yield (url, self.parse_second)

        nextpage = soup.find('span', class_ = 'next').a
        if nextpage:
            nexturl = self.start_url + nextpage['href']
            yield (nexturl, self.parse_first)
        else:
            self.running = False # 表明运行到这里则不会继续添加待爬URL队列

    def parse_second(self, url):
        r = requests.get(url)
        soup = BeautifulSoup(r.content, 'lxml')
        mydict = {}
        title = soup.find('span', property = 'v:itemreviewed')
        mydict['title'] = title.text if title else None
        duration = soup.find('span', property = 'v:runtime')
        mydict['duration'] = duration.text if duration else None
        time = soup.find('span', property = 'v:initialReleaseDate')
        mydict['time'] = time.text if time else None
        yield mydict


if __name__ == '__main__':
    douban = DouBan()
    douban.run()

可以看到这个使用方式和scrapy非常相似

  • 继承类,只需要写解析函数(因为是简易框架,因此还需要写请求函数)
  • 用yield返回数据或者新的请求及回调函数
  • 自动多线程(scrapy是异步)
  • 运行都一样只要 run
  • 可以设置是否存储到文件等,只是没有考虑可扩展性(数据库等)

下面我们来说一说它是怎么实现的

我们可以对比下面两个版本,一个是 上一篇文章 中的使用方法,另一个是进行了一些修改,将一些功能抽象出来,以便扩展功能。

上一篇文章版本代码请读者自行点击链接去看,下面是修改后的版本代码。

import requests
import time
import threading
from queue import Queue, Empty
import json
from bs4 import BeautifulSoup

def run_time(func):
    def wrapper(*args, **kw):
        start = time.time()
        func(*args, **kw)
        end = time.time()
        print('running', end-start, 's')
    return wrapper


class Spider():

    def __init__(self):
        self.start_url = 'https://movie.douban.com/top250'
        self.qtasks = Queue()
        self.data = list()
        self.thread_num = 5
        self.running = True

    def start_requests(self):
        yield (self.start_url, self.parse_first)

    def parse_first(self, url):
        r = requests.get(url)
        soup = BeautifulSoup(r.content, 'lxml')

        movies = soup.find_all('div', class_ = 'info')[:5]
        for movie in movies:
            url = movie.find('div', class_ = 'hd').a['href']
            yield (url, self.parse_second)

        nextpage = soup.find('span', class_ = 'next').a
        if nextpage:
            nexturl = self.start_url + nextpage['href']
            yield (nexturl, self.parse_first)
        else:
            self.running = False


    def parse_second(self, url):
        r = requests.get(url)
        soup = BeautifulSoup(r.content, 'lxml')
        mydict = {}
        title = soup.find('span', property = 'v:itemreviewed')
        mydict['title'] = title.text if title else None
        duration = soup.find('span'






请到「今天看啥」查看全文