python 多进程 批量下载 pdf 文件

背景

由于最近正在学习斯坦福大学的编译原理网课,所以需要使用其配套的pdf课件。
根据b站热心网友提供的课件链接http://openclassroom.stanford.edu/MainFolder/DocumentPage.php?course=Compilers&doc=docs/slides.html
可以看到需要下载的资源实在是太多了,手动点开下载要到何时。。。(之前学习李宏毅的线性代数,就深受这种龟速、原始人下载文件的方法困扰过)。因此,重拾并且借此机会深入学习爬虫、进程操作的技巧。

正题(说一下大概的思路)

  1. 爬取一个网页之前,先摸清其网页构架。这个课程网页极其简单(似乎外面大学CS类的课程网页都很简洁。。。)没有复杂的DOM,所以这里解析html的解析部分应该算是最基础的,如果大家要尝试其他复杂网页的爬取,可能会需要正则表达式等更高级的技巧。可以参考https://germey.gitbooks.io/python3webspider/
  2. 刚开始,我只是使用了简单的串行爬取,结果速度真的是一言难尽。所以,决定尝试使用多进程的方法。但是,起初我脑子里一直想着多线程。这是由于之前刚开始接触爬虫的时候,有被要求学多线程,但是感觉这里使用多线程,比较复杂,总觉得哪里不大对劲。。。最后在廖雪峰的教程上发现,多进程更符合自己的想法,所以就学习使用进程池的方法,下载文件。
  3. 由于这个网站没有反爬机制,所以就很顺利地实现多进程下载文件。如果碰到有反爬机制(一般来说,是爬取结果出现乱码或者返回值为空),可以参考一下这个教程https://heli06.github.io/category/bilibili%E5%A4%9A%E8%BF%9B%E7%A8%8B%E7%88%AC%E8%99%AB/

代码(大体思路,创造4个进程 ,分别对应一个文件的下载)

from bs4 import BeautifulSoup
import requests
import urllib.request
from multiprocessing import pool
import os
import time
#每个子进程执行地函数,即下载pdf文件
def run_proc(url,name):
    try:
        start=time.time()
        #在urlretrieve卡了一会,主要是没有明白filename参数的意思。
        #千万不可以只写路径名,这里要求给出下载后的文件所存放的路径和其名字,否则会报permission error
        #replace部分是自行命名的结果,重点是一定要有命名
        urllib.request.urlretrieve(url,'...(填入路径名称)...\\{}'.format(name).replace('slides/',''))
        end=time.time()
        print('File:{} has been done after {} seconds'.format(name,end-start))
    except Exception as identifier:
        print('error type',identifier.__class__.__name__)
        print('error details:',identifier)
#常规获取网页html操作,细节自行搜索
def getHtmlCode(url):
    headers={'User-Agent':' Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
    #URL加入请求头,伪装成浏览器放送请求
    URL=urllib.request.Request(url,headers=headers)
    #从回复信息中提取html文件并解码
    page=urllib.request.urlopen(URL).read().decode()
    return page
def getimg(page):
        #以lxml的方式解析response数据
    soup=BeautifulSoup(page,'lxml')
    block_list=soup.find_all('td',class_='icon')
    item_a_tag_name=''
    print('parent pid:{}'.format(os.getpid()))
    p=pool.Pool()
    for block in block_list:
        item_a_tag_name=block.find('a')
        if(item_a_tag_name):
            # 由于我只想下载老师做完笔记的课件,所以这里做了筛选
            if (item_a_tag_name.find('img').get('alt') == 'Annotated slides'):
                block_url='http://openclassroom.stanford.edu/MainFolder/courses/Compilers/docs/'+item_a_tag_name.get('href')
                # 将下载url分给一个进程处理
                p.apply_async(run_proc,args=(block_url,item_a_tag_name.get('href')))
    print('waiting all proc to be done...')
    p.close()
    p.join()
    print('all done')
def main():
    i=1
    url='http://openclassroom.stanford.edu/MainFolder/DocumentPage.php?course=Compilers&doc=docs/slides.html'
    page=getHtmlCode(url)
    getimg(page)
if __name__ == '__main__':
    main()

总结

  • 使用多进程后,下载速度感人。。。
  • 巩固了一下爬虫相关库、函数的用法,总算搞清了之前困扰的find_all和find的区别,和一些坑(比如使用if过滤nonetype)

你可能感兴趣的:(python 多进程 批量下载 pdf 文件)