爬虫

多线程爬虫

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

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都已支持),甚至分布式...
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,893评论 2 89
  • 简介 MongoDB 是一个基于分布式文件存储的NoSQL数据库 由C++语言编写,运行稳定,性能高 旨在为 WE...
    大熊_7d48阅读 35,884评论 1 9
  • 一、收获的4个知识点 1、组织经验萃取标准:可模仿、可复制、能分解、能应用。 这个道理说出来我们都懂,但是实际萃取...
    Ange_7ea4阅读 7,127评论 0 1
  • iOS事件处理- 用户使用App产生的事件及响应方法: iOS中不是任何对象都能处理事件,只有继承UIRespon...
    ValienZh阅读 327评论 0 0
  • 计划执行又放下,然后过一阵子又开始又放下……如此循环,一次次给自己找动力,自己恨自己意志薄弱。 然而作者认为,动力...
    Tianjiejie阅读 110评论 0 0