python 多线程爬虫问题

2016-06-13 00:06:21 +08:00
 practicer

我刚入门 python ,最近想要写爬虫爬取豆瓣图书信息。目前已完成以下函数附说明:

初始页面是 https://book.douban.com/tag/%E7%BC%96%E7%A8%8B

1.pages = fetchPages() # 获取初始页面的翻页链接,返回所有翻页链接的列表

2.books = fetchBooks(pages) # 获取初始页面及所有翻页页面的书籍网址,返回所有书籍链接的列表

3.data = fetchBookInfo(books) # 获取所有书籍的信息,信息包含书名、评分等,返回包含书籍信息的元组组成的列表

4.savingCsv(data) # 将所有书籍信息写入 csv 文件

可以看到每个函数接受上一个函数返回的结果。

我的问题是,怎样可以把这些函数变成多线程处理,我在网上花了点时间搜索没有找到答案,也许多线程属于高级主题,对我这种初学者来说理解比较困难,请网友不吝赐教。

7482 次点击
所在节点    Python
33 条回复
itlr
2016-06-13 02:27:55 +08:00
步骤 1 后可以用 multiprocessing 对各个 page 并行采集,用 Pool , starmap_async()这样的调用,具体要参考文档 https://docs.python.org/2/library/multiprocessing.html
YUX
2016-06-13 03:42:59 +08:00
from concurrent.futures import ThreadPoolExecutor
from requests_futures.sessions import FuturesSession
session = FuturesSession(executor=ThreadPoolExecutor(max_workers=20))
import requests
from bs4 import BeautifulSoup
import re

def fetchPages(first_page):
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
content = requests.get(first_page, headers=headers).text
soup = BeautifulSoup(content, "html.parser")
a_tags_final = soup.find("div", { "class" : "paginator" }).find_all("a")[-2].get("href")
page_max = int(re.findall("start=(.*)&",a_tags_final)[0])
pages = []
for k in range(0,page_max+20,20):
pages.append(first_page+"?start="+str(k))
return pages


def fetchBooks(pages):
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
books = []
for page in pages:
books.append(session.get(page, headers = headers))
def get_books_url(book):
soup = BeautifulSoup(book, "html.parser")
book_list = list(map(lambda li: li.find("div", { "class" : "info" }).find("h2").find("a").get("href"), soup.find_all("li", { "class" : "subject-item" })))
return book_list
books = list(map(lambda book: get_books_url(book.result().text), books))
books_url = []
for book in books:
books_url += book
return books_url



def fetchBookInfo(books):
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
books_info = []
for book in books:
books_info.append(session.get(book, headers = headers))
def get_books_data(book_info):
soup = BeautifulSoup(book_info, "html.parser")
info = soup.find("div", { "id" : "info" })
return info
book_data = list(map(lambda book: get_books_data(book.result().text), books_info))
return book_data



if __name__ == '__main__':
pages = fetchPages("https://book.douban.com/tag/%E7%BC%96%E7%A8%8B")
books = fetchBooks(pages)
data = fetchBookInfo(books)
YUX
2016-06-13 03:46:05 +08:00
Python3.5 运行通过 需要 BeautifulSoup 和 requests_futures
max_workers=20 这里根据你的需要你自己改一下
我只写到了 data = fetchBookInfo(books)这一步,怎么弄这些个数据就看你了

其实有用的只有一句话 用 requests_futures
https://github.com/ross/requests-futures
practicer
2016-06-13 08:29:50 +08:00
@itlr
@YUX
很感谢两位的指教,能不能再帮我指点一下这种需求属于多线程还是多进程。
ila
2016-06-13 08:51:19 +08:00
试试了,不同图书分类一个进程
araraloren
2016-06-13 09:46:01 +08:00
~~把每个步骤放在一个线程里面就是多线程了,不过要注意公共数据的访问可能需要互斥
刚入门 python ,还是先来一个模块化的吧,然后学习多线程改进程序
wbt
2016-06-13 10:08:05 +08:00
Python 是多线程性能并不好
先一个线程试试吧,不行就开多个进程。
qianbaooffer
2016-06-13 10:10:33 +08:00
对于这种网络 io,python 多线程对 GIL 做了优化,性能没有问题,如果不是 IO 类处理,那多线程确实有问题
@wbt
wbt
2016-06-13 10:44:31 +08:00
@qianbaooffer 学习了~
laoni
2016-06-13 10:52:01 +08:00
PY scipy 为啥不用。。。。还自己写。。 一直跑 scipy 相当稳定靠谱。。
Allianzcortex
2016-06-13 11:07:44 +08:00
用 multiprocessing 库, Queue 来实现 FIFO 的任务队列,当时爬的是拉钩,自己之前写过一个学习用的 demo ,比较简答,有注释,可以直接套用:

<script src="https://gist.github.com/Allianzcortex/99effde0ae0e4ddb51411262c6675e50.js"></script>
practicer
2016-06-13 11:19:34 +08:00
@araraloren
@YUX
@itlr
@ila
@wbt
@qianbaooffer

我的 1 、 2 、 3 个函数里面都设置了等待时间,也就是爬 page 链接的时候等一段时间,爬 book 链接的时候也等一段时间,爬 book 信息的时候还是会等一段时间,这样做是为了不给对方太大压力,虽然我知道我的小爬虫根本不会给他们带来任何负担,但这就是我的原则吧。我想改进的地方是,如何让这三个函数之间有(异步|多线程|多进程)处理的可能,从而改善爬虫的速度
practicer
2016-06-13 11:23:31 +08:00
@Allianzcortex 看起来不错喔,刚好在看 multiprocessing 和 queue 的手册,冥冥中感觉到是我想要的,感谢分享。
Jblue
2016-06-13 11:30:54 +08:00
1 可以单独抽出来,把所有的需爬 url 去重之后集中放在一起(比如队列),然后 23 放在一起,每个线程从队列中获取一个 url 单独消化。
EchoUtopia
2016-06-13 12:41:15 +08:00
practicer
2016-06-13 13:52:23 +08:00
@Jblue 做你说的多线程和队列要用到哪些库和方法?能详细说一下吗?
geek123
2016-06-13 13:54:49 +08:00
@practicer 我们网站上有个网友写过一个 free 的课程,你可以看看。

http://www.hubwiz.com/course/570dce425fd193d76fcc723d/
YUX
2016-06-13 13:59:02 +08:00
@practicer requests futures 有 ThreadPoolExecutor 和 ProcessPoolExecutot 两个用法
用 max worker 直接控制频率多好
louk78
2016-06-13 14:16:14 +08:00
如果有 A,B,C,D 四件事情,单线程是一件事情完成之后在做另外一件事情,而多线程则可以, a 线程做 A 事情, b 线程做 B 事情, c 线程做 C 事情,d 线程做 D 事情,这四件事情可以同时做,当然有做的快的,也有做的慢,四个线程可以看出四个人,四件事情可以看成,喂孩子吃奶,做饭,扫地,洗碗
JhOOOn
2016-06-13 16:06:34 +08:00
学爬虫一定是 python , 爬网站一定是 douban , douban :“我特么的招谁惹谁了”

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/285244

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX