爬虫

作者: WANGLIN_HZ | 来源:发表于2018-12-01 15:37 被阅读0次

    多线程爬虫

    有些时候,比如下载图片,因为下载图片是一个耗时的操作。如果采用之前那种同步的方式下载。那效率肯会特别慢。这时候我们就可以考虑使用多线程的方式来下载图片。

    Queue(队列对象) Queue是python中的标准库,可以直接import Queue引用;

    队列是线程间最常用的交换数据的形式
    对于资源,加锁是个重要的环节。因为python原生的list,dict等,都是not thread safe的。而Queue,是线程安全的,因此在满足使用条件下,建议使用队列

    包中常用的方法
    • Queue.qsize() 返回队列的大小
    • Queue.empty() 如果队列为空,返回True,反之False
    • Queue.full() 如果队列满了,返回True,反之False
    • Queue.full 与 maxsize 大小对应
    • Queue.get([block[, timeout]])获取队列,timeout等待时间

    迭代器

    迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

    可迭代对象 我们已经知道可以对list、tuple、str等类型的数据使用for...in...的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代

    可迭代对象的本质 可迭代对象进行迭代使用的过程,每迭代一次(即在for...in...中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。

    可迭代对象通过iter方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据.

    • 一个具备了 iter 方法的对象,就是一个 可迭代对象
    iter()函数与next()函数

    list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。iter()函数实际上就是调用了可迭代对象的iter方法。

    • 迭代器Iterator
      迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的next方法。所以,我们要想构造一个迭代器,就要实现它的next方法。并且python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现iter方法,迭代器的iter方法返回自身即可。

      一个实现了iter方法和next方法的对象,就是迭代器。

    • for...in...循环的本质
      for item in Iterable 循环的本质就是先通过 iter()函数 获取 可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用 next() 方法来获取下一个值并将其赋值给item,当遇到 StopIteration 的异常后循环结束。

    生成器
    • 生成器 利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器。
    • 使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
    • yield关键字有两点作用:
    • 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
    • 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
    • 可以使用next()函数让生成器从断点处继续执行.
    协程

    协程,英文叫做 Coroutine,又称微线程,纤程,协程是一种用户态的轻量级线程。

    协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此协程能保留上一次调用时的状态,即所有局部状态的一个特定组合,每次过程重入时,就相当于进入上一次调用的状态。

    协程本质上是个单进程,协程相对于多进程来说,无需线程上下文切换的开销,无需原子操作锁定及同步的开销,编程模型也非常简单。

    我们可以使用协程来实现异步操作,比如在网络爬虫场景下,我们发出一个请求之后,需要等待一定的时间才能得到响应,但其实在这个等待过程中,程序可以干许多其他的事情,等到响应得到之后才切换回来继续处理,这样可以充分利用 CPU 和其他资源,这就是异步协程的优势。

    • 协程和线程差异
      在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住
    greenlet

    为了更好使用协程来完成多任务,python中的greenlet模块对其封装,从而使得切换任务变的更加简单

    gevent
    • greenlet已经实现了协程,但是这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent

    • 其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。

    • 由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO

    • 进程是资源分配的单位

    • 线程是操作系统调度的单位

    • 进程切换需要的资源很最大,效率很低

    • 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)

    • 协程切换任务资源很小,效率高

    • 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中所以是并发的


    JavaScript

    JavaScript 是网络上最常用也是支持者最多的客户端脚本语言。它可以收集 用户的跟踪数据,不需要重载页面直接提交表单,在页面嵌入多媒体文件,甚至运行网页游戏。
    我们可以在网页源代码的<scripy>标签里看到,比如:

    <script type="text/javascript" src="https://statics.huxiu.com/w/mini/static_2015/js/sea.js?v=201601150944"></script>
    
    jQuery

    jQuery 是一个十分常见的库,70% 最流行的网站(约 200 万)和约 30% 的其他网站(约 2 亿)都在使用。一个网站使用 jQuery 的特征,就是源代码里包含了 jQuery 入口,比如:

    src="https://statics.huxiu.com/w/mini/static_2015/js/jquery-1.11.1.min.js?v=201512181512"></script>
    
    Ajax

    我们与网站服务器通信的唯一方式,就是发出 HTTP 请求获取新页面。如果提交表单之后,或从服务器获取信息之后,网站的页面不需要重新刷新,那么你访问的网站就在用Ajax 技术。

    Ajax 其实并不是一门语言,而是用来完成网络任务(可以认为 它与网络数据采集差不多)的一系列技术。Ajax 全称是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML),网站不需要使用单独的页面请求就可以和网络服务器进行交互 (收发信息)。

    DHTML

    Ajax 一样,动态 HTML(Dynamic HTML, DHTML)也是一系列用于解决网络问题的 技术集合。DHTML 是用客户端语言改变页面的 HTML 元素(HTML、CSS,或者二者皆 被改变)。比如页面上的按钮只有当用户移动鼠标之后才出现,背景色可能每次点击都会改变,或者用一个 Ajax 请求触发页面加载一段新内容,网页是否属于DHTML,关键要看有没有用 JavaScript 控制 HTML 和 CSS 元素。


    Selenium

    Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动操作,不同是Selenium 可以直接运行在浏览器上,它支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器)。 Selenium 可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。

    Selenium 自己不带浏览器,不支持浏览器的功能,它需要与第三方浏览器结合在一起才能使用。但是我们有时候需要让它内嵌在代码中运行,所以我们可以用一个叫 PhantomJS 的工具代替真实的浏览器。

    Selenium 库里有个叫 WebDriver 的 API。WebDriver 有点儿像可以加载网站的浏览器,但是它也可以像 BeautifulSoup 或者其他 Selector 对象一样用来查找页面元素,与页面上的元素进行交互 (发送文本、点击等),以及执行其他动作来运行网络爬虫。

    注意: 我们使用的有界面浏览器,它虽然方便我们观察,但是在实际运用中是非常消耗性能的 我们也可以使用Chrome的无界面浏览器,除了没有浏览器界面以外,其它的相关操作都与有界面浏览器相同

    • 获取id标签值

    element = driver.find_element_by_id("passwd-id")

    • 获取name标签值

    element = driver.find_element_by_name("user-name")

    • 获取标签名值

    element = driver.find_elements_by_tag_name("input")

    • 也可以通过XPath来匹配

    element = driver.find_element_by_xpath("//input[@id='passwd-id']")

    Cookies

    获取页面每个Cookies值,用法如下:

    cookies = driver.get_cookies()
    for cookie in cookies:
        print("%s -> %s" % (cookie['name'], cookie['value']))
    
    cookie_dict = {i['name']:i['value'] for i in cookies}
    print(cookie_dict)
    
    添加cookies

    driver.add_cookie(cookie_dict)

    删除Cookies,用法如下
    • 删除一个特定的cookie
      driver.delete_cookie("CookieName")
    • 删除所有cookie
      driver.delete_all_cookies()
    设置无头浏览器

    opt = webdriver.ChromeOptions()
    opt.set_headless()

    设置代理

    opt = webdriver.ChromeOptions()
    opt.add_argument("--proxy-server=http://118.20.16.82:9999")


    数据库基本命令
    • 查看当前数据库名称:db
    • 列出所有在物理上存在的数据库:show dbs
    • 切换数据库 如果数据库不存在,则指向数据库,但不创建,直到插入数据或创建集合时数据库才被创建:use 数据库名称
    • 查看当前数据库信息:db.stats()
    • 数据库删除:删除当前指向的数据库,如果数据库不存在,则什么也不做:db.dropDatabase()
    创建集合

    :db.createCollection(name, options)

    • 不限制集合大小:db.createCollection("stu")
    查看当前数据库所有集合
    • show collections:当前数据库的集合数
    删除集合
    • db.集合名称.drop() 如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false
    数据的增、删、改、基本查询
    插入文档

    语法

    db.集合名称.insert(document)

    插入文档时,如果不指定_id参数,MongoDB会为文档分配一个唯一的ObjectId

    db.stu.insert({name:'张三',gender:1})

    多条插入

    db.stu.insert([{name:'小明',gender:1},{name:'小红',gender:0}])

    查询全部文档

    语法: db.集合名称.find()

    db.stu.find()

    更新文档

    全文档更新

    db.stu.update({name:'xxxxx'},{name:'张xxx'})

    指定属性更新,通过操作符$set

    db.stu.insert({name:'李四',gender:1}) db.stu.update({name:'李四'},{$set:{name:'李四'}})

    save() 方法
    save() 方法通过传入的文档来替换已有文档,如果文档的_id已经存在则修改,如果文档的_id不存在则添加 db.集合名称.save(document)

    db.stu.save({_id:'20180820101010','name':'保存',gender:1})

    删除文档

    • db.集合名称.remove(document)
    • 只删除1条,1表示是否只删除一条为true的意思 db.集合名称.remove(document,1) db.集合名称.remove(document,{justOne:true})
    • 表示删除全部 db.集合名称.remove({})

    查询:

    db.集合名称.find({条件文档})

    方法findOne():查询,只返回第一个

    db.集合名称.findOne({条件文档})

    方法pretty():将结果格式化

    db.集合名称.find({条件文档}).pretty()

    查询出姓名等于李某某的学生

    db.stu.find({name:'李某某'})


    Limit与Skip方法

    limit() 方法 读取指定数量的数据记录
    基本语法如下所示:

    db.COLLECTION_NAME.find().limit(num)

    Skip() 方法 使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。
    基本语法格式如下:

    db.集合名称.find().skip(num)

    limit() 方法、Skip() 方法 同时使用,不分先后顺序 表示跳过多少条,返回多少条
    查询第5至8条数据

    db.stu.find().limit(4).skip(5)

    sort() 方法排序

    sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。

    基本语法如下所示:
    升序

    db.集合名称.find().sort({要排序的字段:1})

    降序

    db.集合名称.find().sort({要排序的字段:-1})

    根据多个字段排序: 例:先根据年龄做降序,再根据性别做升序

    db.集合名称.find().sort({age:-1,gender:1})

    Scrapy 框架
    • Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛。
    • 框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便。
    • Scrapy 使用了 Twisted['twɪstɪd] 异步网络框架来处理网络通讯,可以加快我们的下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。

    异步:调用在发出之后,这个调用就直接返回,不管有无结果 非阻塞:关注的是程序在等待调用结果(消息,返回值)时的状态,指在不能立刻得到结果之前,该调用不会阻塞当前线程

    • Scrapy Engine(引擎): 负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。
    • Scheduler(调度器): 它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。
    • Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理,
    • Spider(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器),
    • Item Pipeline(管道):它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方.
    • Downloader Middlewares(下载中间件):你可以当作是一个可以自定义扩展下载功能的组件。
    • Spider Middlewares(Spider中间件):你可以理解为是一个可以自定扩展和操作引擎和Spider中间通信的功能组件(比如进入Spider的Responses;和从Spider出去的Requests)

    制作 Scrapy 爬虫 一共需要4步:

    • 新建项目

    scrapy startproject 爬虫项目名称

    • 新建一个新的爬虫

    • 明确目标

    编写items.py):明确你想要抓取的目标

    • 制作爬虫

    scrapy genspider 爬虫文件名称 域名:制作爬虫开始爬取网页

    • 存储内容

    pipelines.py):设计管道存储爬取内容

    关于爬虫部分一些建议:
    • 尽量减少请求次数,能抓列表页就不抓详情页,减轻服务器压力,程序员都是混口饭吃不容易。
    • 当web网站反爬虫手段比较严格时,不要只看 Web 网站,还有手机 App 和 H5,这样的反爬虫措施一般比较少。
    • 实际应用时候,之前一般防守方做到根据 IP 限制频次就结束了,除非很核心的数据,不会再进行更多的验证,毕竟成本的问题会考虑到还要考虑到用户体验。
    • 如果真的对性能要求很高,可以考虑多线程、多进程(一些成熟的框架如 Scrapy都已支持),甚至分布式...

    相关文章

      网友评论

          本文标题:爬虫

          本文链接:https://www.haomeiwen.com/subject/srxrcqtx.html