文档章节

线程池实现爬虫

alazyer
 alazyer
发布于 2016/09/06 20:19
字数 705
阅读 107
收藏 0

3 月,跳不动了?>>>

偶然间学习实验楼课程, 看到的, 想到自己刚刚出来找工作的时候, 面试过一家公司让自己实现线程池, 并利用实现的线程池进行多线程爬取, 当时水平太菜(现在也很菜, 不过比那个时候好多了). 这里记录下, 当时没有理解的是queue.Queue().task_done(), 现在想想就是这个线程的任务处理完了, 通知queue.Queue(), 让他知道这个处理完了, 别的线程调用queue.Queue().get()时, 不会有这个处理过的element出现, 让其他线程处理.

这里将实验楼的代码贴过来, 权当留念了. 不过代码有好多可以改进或者说有更好的方式实现. 比如:

  • 使用requests而不是socket来爬取网站内容
  • 使用BeautifulSoup或者xpath来解析爬取的网页内容中的链接信息
from queue import Queue 
from threading import Thread, Lock
import urllib.parse
import socket
import re
import time

seen_urls = set(['/'])
lock = Lock()


class Fetcher(Thread):
    def __init__(self, tasks):
        Thread.__init__(self)
        self.tasks = tasks
        self.daemon = True

        self.start()

    def run(self):
        while True:
            url = self.tasks.get()
            print(url)
            sock = socket.socket()
            sock.connect(('localhost', 3000))
            get = 'GET {} HTTP/1.0\r\nHost: localhost\r\n\r\n'.format(url)
            sock.send(get.encode('ascii'))
            response = b''
            chunk = sock.recv(4096)
            while chunk:
                response += chunk
                chunk = sock.recv(4096)

            links = self.parse_links(url, response)

            lock.acquire()
            for link in links.difference(seen_urls):
                self.tasks.put(link)
            seen_urls.update(links)    
            lock.release()

            self.tasks.task_done()

    def parse_links(self, fetched_url, response):
        if not response:
            print('error: {}'.format(fetched_url))
            return set()
        if not self._is_html(response):
            return set()
        urls = set(re.findall(r'''(?i)href=["']?([^\s"'<>]+)''',
                              self.body(response)))

        links = set()
        for url in urls:
            normalized = urllib.parse.urljoin(fetched_url, url)
            parts = urllib.parse.urlparse(normalized)
            if parts.scheme not in ('', 'http', 'https'):
                continue
            host, port = urllib.parse.splitport(parts.netloc)
            if host and host.lower() not in ('localhost'):
                continue
            defragmented, frag = urllib.parse.urldefrag(parts.path)
            links.add(defragmented)

        return links

    def body(self, response):
        body = response.split(b'\r\n\r\n', 1)[1]
        return body.decode('utf-8')

    def _is_html(self, response):
        head, body = response.split(b'\r\n\r\n', 1)
        headers = dict(h.split(': ') for h in head.decode().split('\r\n')[1:])
        return headers.get('Content-Type', '').startswith('text/html')


class ThreadPool:
    def __init__(self, num_threads):
        self.tasks = Queue()
        for _ in range(num_threads):
            Fetcher(self.tasks)

    def add_task(self, url):
        self.tasks.put(url)

    def wait_completion(self):
        self.tasks.join()

if __name__ == '__main__':
    start = time.time()
    pool = ThreadPool(4)
    pool.add_task("/")
    pool.wait_completion()
    print('{} URLs fetched in {:.1f} seconds'.format(len(seen_urls),time.time() - start))

总结:

  • 线程池就是一个包含多个线程的容器, 内部有多个线程, 现成一直处于工作状态.
  • 线程处于工作状态的意思是说: 当任务队列中有任务时, 线程从任务队列中取得任务, 然后进行处理, 并将任务结果以合理的方式再次反馈到任务队列中; 当任务队列中没有任务时, 则阻塞, 等待其他线程向任务队列添加任务或者通过调用线程池的add_task添加初始任务.
  • 调用任务队列的join方法实现阻塞等待, 当所有任务都已经执行完成后(任务队列为空, 并且所有现成都处于阻塞状态), 输出统计信息.

内容都是个人理解, 如有出入, 请赐教.

© 著作权归作者所有

alazyer
粉丝 5
博文 68
码字总数 35105
作品 0
程序员
私信 提问
加载中

评论(0)

多种方法实现 python 线程池

最近在做一个爬虫相关的项目,单线程的整站爬虫,耗时真的不是一般的巨大,运行一次也是心累,,,所以,要想实现整站爬虫,多线程是不可避免的,那么python多线程又应该怎样实现呢?这里主要...

元谷
2019/10/31
77
0
Memcache-LRU爬虫线程-源码分析

介绍 memcache 中实现了内存管理模型用来存储数据,而在此基础上又实现了一套LRU爬虫模型来维护这些已使用的内存,因为如果一直使用这些内存而不去维护会占用大量的系统资源,所以提供这么一...

简单方式
2017/02/04
0
0
Java爬虫抓取知乎20万用户信息并做简易分析

前段时间看@路人甲 分享了一篇爬取知乎用户的文章,心血来潮,想着也该把自己很早写的知乎爬虫完善一下 趁着每天实习回来还有点时间,整理了下思路和原来的代码 因为自己不太爱用框架,所以爬...

KKys
2017/01/21
0
0
XXL-CRAWLER v1.2.1 发布,分布式爬虫框架

版本新特性 JS渲染:支持JS渲染方式采集数据,可参考 "爬虫示例6"; 抽象并设计PageLoader,方便自定义和扩展页面加载逻辑,如JS渲染等。底层提供 "JsoupPageLoader(默认/推荐)","HtmlUnit...

许雪里
2018/02/08
1.3K
18
virjar/vscrawler

vscrawler vscrawler是一个更加适合抓取的爬虫框架,他不是教科书似的爬虫,准确说他不是爬虫,没有广度优先遍历这些说法,他所面临的网站URL不是网络里面的网络拓扑图而是一个个目标明确的抓...

virjar
2017/06/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

什么是反射,为什么有用? - What is reflection and why is it useful?

问题: What is reflection, and why is it useful? 什么是反射,为什么有用? I'm particularly interested in Java, but I assume the principles are the same in any language. 我对Jav......

技术盛宴
39分钟前
19
0
SSM框架整合

mybatis逆向工程 mybatis-generator生成pojo、mapper接口及映射文件 mapper放到e3-manager-dao层中 导入sql到数据库中; 导入逆向工程工具,配置xml文件 运行main方法 重复运行main不会覆盖!...

七宝1
今天
30
0
OSChina 周日乱弹 —— 和网友的第一次开房经历

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @薛定谔的兄弟 :分享洛神有语创建的歌单「我喜欢的音乐」: 《Ljósið》- Ólafur Arnalds 手机党少年们想听歌,请使劲儿戳(这里) @xiaos...

小小编辑
今天
54
0
程序员职业生涯指引

程序员应该尽早规划自己的职业生涯 为什么写 众所周知 IT 这一行到了一定的年龄、大部分人都或多或少有危机感,特别是今年全国乃至全球发生的疫情、导致整体经济受到很大的影响、这次的疫情影...

科比可比克
今天
11
0
JVM调优实战分析

一、查看服务器项目JVM参数以及参数分析 1、jps 命令 : 列出系统中所有的 Java 应用程序以及PID 如下图所示,26647就是我部署在服务器的一个小项目的 PID 2、jmap命令:查看堆的使用情况 如...

IT-Mamba
今天
46
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部