看了很多范例,但是还是没有理解,比如要爬取 10 页内容,每页有 30 条数据,那么开启 5 个线程的话,我自己尝试写过,不过这 5 个线程都单独爬取 300 条数据,如何才能做到一个线程爬取两页这样?
1
hard2reg 2016-07-17 12:15:38 +08:00
一个线程爬取两页循环啊
|
2
lecher 2016-07-17 12:27:35 +08:00 via Android
需要一个任务队列,抓取线程从任务队列里面取任务抓取。
又或者把抓取功能封装成一个独立的任务,主线程通过直接调用的形式直接分配任务。 |
3
bigtan 2016-07-17 13:02:39 +08:00 1
|
4
wenyu1001 2016-07-17 14:10:05 +08:00
|
5
deadEgg 2016-07-17 15:17:32 +08:00
python 单解释器 无多线程,只有多进程和协程
|
6
deadEgg 2016-07-17 15:18:10 +08:00
推荐用 celery,比较适合你的需求
|
7
neoblackcap 2016-07-17 15:23:02 +08:00
@deadEgg 爬虫明显是 IO 密集型应用,用线程是合适的,推荐协程的,你知道协程就是一种特殊的线程么?怎么就没有线程呢?
|
9
deadEgg 2016-07-17 15:45:07 +08:00
@neoblackcap
因为 Python 有 GIL,当 CPU 密集,多个线程的代码很有可能是线性执行的。不能任意的切换 context ,所以一般用协程或者进程代替,linux 进程开销是非常小的效率某种程度肯定比线程高 “无线程”,只是想说线程比较鸡肋 @beordle py2 有 gevent,3 中有 asyncio 。 不知道你说的没有协程依据在哪? |
10
InkStone 2016-07-17 15:49:06 +08:00
就是当你获得一个 url 的时候,不要直接爬取,而是把它加入一个任务队列。
然后每个线程从任务队列获取要下载的 url |
11
deadEgg 2016-07-17 15:55:00 +08:00
|
14
neoblackcap 2016-07-17 16:16:56 +08:00 via iPhone
@deadEgg 爬虫这种东西我真看不出有什么需要线性执行,而且线性执行的话,你用进程不上锁,不同步?
Linux 的进程是轻量化,但线程的创建成本更低。实在搞不懂你所说的进程成本更低在何处。 在 90%都是网络 IO 的情况下,搞不懂为什么不用线程?这里又不需要大量的计算,进行一次上下文切换会比动不动就上百毫秒的网络请求成本更高? |
15
neoblackcap 2016-07-17 16:25:47 +08:00 via iPhone
@deadEgg 我承认协程更应该是用户态线程,但 Python 的实现真的是用户态线程么?目前各类实现中大多还是用 1:1 模型,你说的协程在这里比线程优秀多少我还是很怀疑的
|
16
jugelizi 2016-07-17 16:31:54 +08:00 1
多进程吧
可以四核一起跑 |
17
deadEgg 2016-07-17 16:38:47 +08:00
@neoblackcap 你讲的是悖论,非用户态线程就不存在 GIL 问题
|
18
neoblackcap 2016-07-17 16:47:31 +08:00 via iPhone
@deadEgg 你为什么这么执着 GIL ?你在网络请求的时候不等待吗?等待的时候上下文切换,有没有 GIL 又有什么问题?
|
19
em70 2016-07-17 16:56:38 +08:00
要做多线程,先把任务分成多个子任务啊,让每个线程负责一个,你不分配子任务,线程又不是智慧生物,它又不会自己协作
|
20
justou 2016-07-17 17:01:36 +08:00
windows 下 IO 密集型任务(不单只网络 IO)优先考虑线程, 用进程太浪费, 每个进程都是一个单独的解释器(1000 个线程跟 1000 个进程区别应该还是较大的, 内存方面)
用队列传输消息, 设计好程序结构, Process 跟 Thread 的切换也就改改几个 import, 具体问题 profile 看下, 谁的效率高(内存使用, 执行时间)就用谁 协程是在一个线程中切换的, 协程切换比线程切换更流畅, 花销也更小, 这是听来的, 没在实际中用过(错了请纠正我), 习惯了队列传输模式, 也方便线程改进程 Unix 下不清楚, 不乱说 |
21
hard2reg 2016-07-17 17:31:32 +08:00
好吧。。。是我理解错了
|
22
wizardforcel 2016-07-17 17:40:45 +08:00
每个线程拿到自己的编号,然后根据编号排数据。考虑你说的按页面编排,每个线程从“起始页+线程编号*2 ”开始抓,步长为“线程总数*2 ”。不要用一个全局的变量来记录当前位置,锁的开销很大。
|
24
practicer 2016-07-17 19:29:49 +08:00 6
最近一个月一直在理清爬虫多任务化的问题,结论是在 python 爬虫领域,实现多任务的正确姿势是单线程异步 IO 模型。
在写出同时能爬取多个链接的代码前,楼主必须先理解这个模型的原理:单线程异步 IO 模型的基础---- [事件循环+回调函数] 模型。 先说事件循环,它是一个系统,这个系统内由以下函数组成: 1.连接服务器的函数, 2.发送 GET 请求到服务器的函数, 3.接收并读取服务器响应的函数, 4.最后是解析响应内容用来获取数据的函数; 可以看到这几个函数基本就是我们写普通爬虫代码的一个流程:函数与函数之间都需要等待,也就是说只有函数 1 返回结果后才能执行函数 2 ,函数 2 返回结果后才能执行函数 3 。。。 那么,用事件循环来控制这些函数和写普通爬虫代码有什么不同呢? 答案就是,事件循环可以由程序员手动控制多个爬虫(任务),而不是像多线程那般把分配权交给操作系统随机分配。 当一号爬虫(任务)在执行函数 1 时,一执行完就立即返回(意思就是不等待最后获取值),并将控制权交还给事件循环,交给它之后,开始执行二号爬虫(任务);二号爬虫开始执行函数 1 ,同样,一执行完就立即返回,并将控制权交给事件循环;交给它之后,开始执行三号爬虫(任务),三号爬虫开始执行函数 1 ,同样,一执行完就立即返回。。。以此类推。。 问题来了,当一号爬虫(任务)的函数 1 处理完并返回值后该如何处理这个值?这个时候,回调函数就能派上用场了,回调函数起到通知的作用,告知循环系统在咱们这一号爬虫(任务)有个函数处理完了,要用它返回的结果来执行函数 2 。循环机制听到通知后,便开始执行一号爬虫(任务)的函数 2 。执行函数 2 和执行函数 1 的机制完全相同,也是一执行完就返回,并立即将分配权交给循环机制,这样让循环机制同时地、不停地处理二号、三号、四号。。。爬虫(任务)。 最后,直到一号爬虫获取最终想要爬取的数据,同时,二号、三号、四号。。。爬虫仍在同时工作,没有停止,然后二号爬虫也执行完了所有函数并得到数据,然后是三号、四号。。。 以上就是基于事件循环+回调函数的异步 IO 爬虫模型,虽然是单线程但是效率非常高,像 twisted , tornado 这些流行的异步 IO 库基本都是基于这个模型。但是这种模型也有很多弊端,最令人不爽的两个地方是, 1.调试起来非常恼火,根本看不到 traceback 。 2.一旦事件循环内的函数数量变多,代码逻辑也变的复杂。 So.python 3.4 在基于事件循环+回调函数模型的基础上利用生成器的特性,搞了一套改良版的异步 IO 模型,完美解决了以上两个问题。在 python3.5 进一步迭代,推出了 asyncio 库,再次优化了 python 异步 IO 性能。 目前我会写简单的基于事件循环+回调函数的异步 IO 爬虫,仍在理清基于生成器的异步 IO 模型,如果楼主要深入了解,请参考: http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html ( python 之父写的异步爬虫教程) |
26
bobuick 2016-07-18 16:22:14 +08:00
爬虫直接线程模式啊。辣么多 IO 。
|