/绅士/本子采集器

用Python写爬虫也有一段时间了,最近开始学习一个成熟框架的Scrapy,但今天不说它,今天说一说我学习爬虫过程中造过的最实(sang)用(shi)的一个:绅士本子采集器,恩…

ラクネラ, 魔物娘中的spyder, pixiv_id=53842739.

一开始是这样的,有一天我无意中找到了一个网站(htai.me, 又称绅士图书馆,估计不少绅士都用过),对于这个一开始我是非常excited的(当时表情请脑补四老外),当时我是在床上拿着pad看的,翻了几页后看到不少不错的本,然后就下了几个来欣赏一下,然而看来几本就感觉有点不爽了,首先在pad上云盘下本的速度很慢,而且由于绅士图书馆的图包都有加密,还需要我逐个手动解密,这实在太影响观本体验了,这怎么行,给我再来一沓呀!

当时正好刚学会用selenium控制浏览器,手上有把锤子看什么都像钉子,于是就写了这么个东西,来自动解析页面,提取云盘的链接和密码,控制浏览器下载之,最后在本地解密并把名字改成方便管理的格式,恩… 其实这很简单,但关键问题就是当抓取量大时速度实在不忍直视,于是我用多进程改写了一下,以这个获取逐个本子页面的函数为例,实例化了一个中的进程池来充分利用cpu资源,参考一下multiprocessing的文档很快就能写出这样的代码:

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
def get_urls():
articles_url = []
pages_url = get_pages_url()
p = Pool(configer.cpu_core) # 进程池,参数为cpu核心数目
asy_results = []
for page in pages_url:
#print(page)
asy_results.append(
p.apply_async(get_articles, kwds={'url':page}) # 为进程池添加进程
)
p.close()
p.join() # 等待所有进程结束
for r in asy_results: # 取回异步返回的结果
for url in r.get():
articles_url.append(url)
#print(url)
#articles_url.sort()
return articles_url[1:]
# 每个进程都会调用此函数
def get_articles(url):
soup = BeautifulSoup(urlopen(url, timeout=configer.download_time_limit).read(), 'html.parser' )
page_articles_url = []
arti_node = soup.find_all('article', class_='article clearfix')
for i in arti_node:
page_articles_url.append(i.find('a').attrs['href'])
#print(os.getpid())
return page_articles_url

然后的问题就是要通过获取到的信息把本子下载下来,云盘的页面是js代码加载出来的,并且不知道怎么获取直链,折腾了好久惟一的办法似乎就是模拟浏览器环境,并且还不能用无头的phantomjs,因为其无法处理下载窗口,而casperjs需要使用node.js来控制,而我又没点这个技能点…只好控制Chrome来下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 实例化Chrome控制对象及选项
prefs = {"download.default_directory" : configer.download_path}
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option('prefs', prefs)
browser = webdriver.Chrome(chrome_options=chrome_options)
# 打开链接
browser.get(item['link'])
# 一系列操作,最终下载文件
element = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, 'pwd-input')))
pw_ele = browser.find_element_by_class_name('pwd-input')
pw_ele.send_keys(item['password'])
element = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, 'submit-btn')))
clc_ele = browser.find_element_by_class_name('submit-btn')
clc_ele.click()
element = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.ID, 'download')))
dwn_ele = browser.find_element_by_id('download')
dwn_ele.click()

这个下载器这虽然还可以继续优化,比如添加一些逻辑来自动刷新页面减少卡顿,但这本身并不是很理想的方法,此处希望可以得到菊苣指点。 恩… 效果嘛还是不错的,虽然下载的时候有时页面会卡顿。

成吨的本子...

自从有了本子采集器身体一天不如一天了…恩… 最后附上代码