Python爬虫开发:从入门到实战(微课版)

谢乾坤

前言

  • 本书提供了练习网站,其地址为http://exercise.kingname.info/。

1.1 爬虫

  • 这是由于传统低效率的数据收集手段越来越不能满足当今日益增长的数据需求所导致的。
  • 只要你肯,只要你想,那么就有机会利用数据让梦想走进现实。但是面对互联网这样一个由数据构建而成的海洋,如何有效获取数据,如何获取有效数据都是极其劳神费力、浪费成本、制约效率的事情。

1.2 爬虫可以做什么

  • 只要针对一个页面开发出了爬虫,那么这个爬虫也能爬取基于同一个模板生成的不同页面。这种爬虫称为定向爬虫
  • 正是由于现在的网站大量使用了模板来生成页面,所以爬虫才能够有用武之地。
  • 数据不会说谎,特别是数据量极大的数据,人工伪造的总会和自然生成的存在区别。

1.3 爬虫开发技术

  • 面上大部分的爬虫书籍还停留在这个层面。而另一个层面是“术”的层面,遇到各种反爬虫问题时,应该如何突破,如何隐藏爬虫,如何模拟人的行为,以及遇到没有见过的反爬虫策略时,应该如
  • 因此只要能看懂单词就能看懂Python代码
  • 能达到这个目的,用什么方法都没有问题。关于获取网页,本书主要介绍了Python的两个第三方模块,一个是requests,另一个是爬虫框架Scrapy。关于解析网页内容,本书主要介绍了3种方式——正则表达式、XPath和BeautifulSoup。两种网页获取方式和3

2.1 Python的安装和运行

  • 使用Windows操作系统的读者,可访问https://www.python.org/ftp/python/3.6.1/python-3.6.1-amd64.exe下载Python 3.6.1或者更高版本的安装程序
  • 一定要勾选“Add Python 3.6 to PATH”复选框,

2.3 Python的数据结构和控制结构

  • Python里面的整数和数学里面的整数定义是一样的,Python里面的浮点数可以看作是数学里面的小数。
  • 整数的加、减、乘可以直接在print中进行,也可以通过括号来改变运算的优先级:
  • 这是由于计算机里面浮点数的储存机制导致的。有兴趣的读者可以了解一下浮点数从十进制转化为二进制的原理和结果。
  • 所谓变量,可以理解为一个存放其他数据的盒子。使用变量可以减少重复输入。
  • 虽然在Python 3中可以使用中文作为变量名,但还是建议读者将变量名设置为英文。
  • 任何被单引号或者双引号括起来的内容都可以认为是字符串。字符串也可以赋值给变量。
  • 元组和列表的区别:列表生成以后还可以往里面继续添加数据,也可以从里面删除数据;但是元组一旦生成就不能修改。如果它里面只有整数、浮点数、字符串、另一个元组,就既不能添加数据,也不能删除数据,还不能修改里面数据的值。但是如果元组里面包含了一个列表,那么这个元组里面的列表依旧可以变化。
  • 在大多数编程语言里面,下标都是从0开始的,Python也不例外。第0个元素就是指最左边的元素。
  • 变量名[下标]
  • -1表示最后一个元素,-2表示倒数第2个元素,-3表示倒数第3个元素,以此类推,
  • 省略“开始位置下标”和“结束位置下标”,“步长”取-1,表示倒序输出
  • 元组与元组之间也可以相加,相加表示两个元组拼接起来。
  • 列表与列表之间也可以相加,相加表示两个列表拼接起来。
  • 集合最大的应用之一就是去重。
  • 所有需要在if里面运行的代码都需要添加至少一个空格作为缩进,一般约定俗成用4个空格,从而方便人眼阅读。一旦退出缩进,新的代码就不再属于这个if。
  • (1)在使用and连接的多个表达式中,只要有一个表达式不为真,那么后面的表达式就不会执行。(2)在使用or连接的多个表达式中,只要有一个表达式为真,那么后面的表达式就不会执行。
  • 对于多重条件的判断,需要使用“if...elif...else...”。其中,“elif”可以有0个,也可以有多个,但是else只能有0个或者1个。
  • 使用“if...elif...else...”会让代码显得冗长。如果使用字典改写,代码就会变得非常简洁: state_dict = {'start': 1, 'running': 2, 'offline': 3, 'unknown': 4} code = state_dict.get(state, 5)
  • for x in y: 循环体
  • for循环也可以直接从字符串里面获得每一个字符
  • 在做爬虫的时候会遇到需要把列表展开的情况,常犯的一个错误就是把字符串错当成了列表展开。这个时候就会得到不正常的结果。
  • while循环主要用在不知道循环需要执行多少次的情况。
  • 使用while循环最常遇到的问题就是循环停不下来。
  • 如果要让循环永久运行,那么增加一个延迟时间是非常有必要的。time.sleep()的参数为一个数字,单位为秒。如果不增加这个延迟时间,就会导致循环超高速运行。在爬虫的开发过程中,如果超高速运行,很有可能导致爬虫被网站封锁。
  • continue只会影响到本次循环,后面的循环不受影响。
  • 特别注意:在循环里面还有循环(循环嵌套)的时候,continue和break都只对自己所在的这一层循环有效,不会影响外面的循环。

2.4 函数与类

  • 每用一次变量,就可以少写几十“个”字符。在Python里面,还有一个“函数”,每用一次函数,就可以少写很多“行”代码。
  • 所谓的函数,就是一套定义好的流程:输入数据,得到结果
  • 给机器人演示做菜,就是定义函数的过程;让机器人做菜,就是调用函数;原材料是这个函数的输入参数;回锅肉是这个函数的输出。
  • 因为使用函数可以少写代码。
  • 在函数中,可以使用return将里面的结果返回出来。代码一旦运行到了return,那么函数就会结束,return后面的代码都不会被执行。
  • 这两行是return后面的代码,这两行代码是永远不会被执行的。
  • 一个函数只做一件事情,Python编码规范建议一个函数的函数体不超过20行代码。如果超过了,说明这个函数做了不止一件事情,就应该把这个函数拆分为更小的函数。这也就暗示了在函数体里面也可以调用其他的函数。
  • 函数之间可以串行运行,数据先由一个函数处理,再由另一个函数处理;函数也可以嵌套运行,在一个函数里面调用另一个函数。
  • 在Python里面,函数的参数可以有默认值。当调用函数的时候不写参数时,函数就会使用默认参数
  • 在调用函数的时候,如果指定了参数名,就会把值赋给这个参数;如果没有指定参数名,就会从左到右依次赋值给各个参数。
  • 函数外面的容器类作为参数传递到函数中以后,如果函数修改了这个容器里面的值,那么函数外面的容器也会受到影响。但是函数外面的普通变量作为参数传递到函数中,并且函数修改了这个参数的时候,外面的变量不受影响。
  • 函数可以修改容器类的数据但不能修改普通变量
  • 这个问题涉及Python底层的实现,这里不做深入讨论。
  • 这里有一个知识点,就是当要判断一个变量里面的值是不是None的时候,可以使用“is”这个关键字,也可以使用“==”。一般建议使用“is”关键字,因为速度会比“==”稍微快一些。
  • 在Python里面,一切都是对象
  • 对象有“属性”和“方法”。“属性”就是描述这个对象的各种标签,“方法”就是这个对象可以做的动作。
  • 在类的内部,如果要运行它自己的方法,那么调用的时候需要使用“self.方法名(参数)”的形式。如果要让一个变量在这个类的所有方法里面都能直接使用,就需要把变量初始化为“self.变量名”。
  • 那么如何阅读一个使用面向对象思想开发的程序呢?基本思路如下。① 这个类有哪些属性(看外貌)。② 这个类有哪些方法(能做什么)。③ 这些方法在哪里被调用(做了什么)。④ 这些方法的实现细节(怎么做的)。

2.5 阶段案例——猜数游戏

  • 根据二分法的原理,假设答案的范围是M~N,那么最多猜测log2(M+N)次就能猜测出正确答案。

第3章 正则表达式与文件操作

  • 学好正则表达式,是开发爬虫的第一步。

3.1 正则表达式

  • 正则表达式(Regular Expression)是一段字符串,它可以表示一段有规律的信息。Python自带一个正则表达式模块,通过这个模块可以查找、提取、替换一段有规律的信息。
  • 这个“寻找”的过程,在正则表达式中叫作“匹配”。
  • 使用正则表达式有如下步骤。 (1)寻找规律。 (2)使用正则符号表示规律。 (3)提取信息。
  • 一个星号可以表示它前面的一个子表达式(普通字符、另一个或几个正则表达式符号)0次到无限次。
  • 问号表示它前面的子表达式0次或者1次。注意,这里的问号是英文问号。
  • 问号最大的用处是与点号和星号配合起来使用,构成“.*? ”
  • 反斜杠不仅可以把特殊符号变成普通符号,还可以把普通符号变成特殊符号
  • 正则表达式里面使用“\d”来表示一位数字。
  • 小括号可以把括号里面的内容提取出来。
  • Python的正则表达式模块包含一个findall方法,它能够以列表的形式返回所有满足要求的字符串。
  • 如果包含多个“(.*? )”怎么返回呢?如图3-2所示,返回的仍然是一个列表,但是列表里面的元素变为了元组
  • 多个括号内的内容会以元组形式返回
  • 因此在涉及正则表达式中的标点符号时,最好直接复制粘贴,而不要手动输入。
  • 要匹配的内容存在换行符“\n”。要忽略换行符,就需要使用到“re.S”这个flag。
  • search()的用法和findall()的用法一样,但是search()只会返回第1个满足要求的字符串。一旦找到符合要求的内容,它就会停止查找。
  • .*?的意思就是匹配一个能满足要求的最短字符串。
  • ①“.*”:贪婪模式,获取最长的满足条件的字符串。②“.*? ”:非贪婪模式,获取最短的能满足条件的字符串。
  • ①“.*”:贪婪模式,获取最长的满足条件的字符串。 ②“.*? ”:非贪婪模式,获取最短的能满足条件的字符串。
  • 使用re.compile()的时候,程序内部调用的是_compile()方法;当使用re.finall()的时候,在模块内部自动先调用了_compile()方法,再调用findall()方法。re.findall()自带re.compile()的功能,所以没有必要使用re.compile()。
  • re.py在Python 3安装文件夹下面的Lib文件夹中。
  • 先抓大再抓小的思想会贯穿整个爬虫开发过程,一定要重点掌握。
  • 如果括号里面有其他普通字符,那么这些普通字符就会出现在获取的结果里面。

3.2 Python文件操作

  • 文件路径可以是绝对路径,也可以是相对路径。如果是绝对路径,Linux和Mac OS不能直接使用“~”表示“home目录”,因为Python不认识“~”这个符号。如果非要使用这个符号,需要使用Python的“os”模块
  • 如果文件是在Windows中创建的,并且使用UTF-8打开文件出现了乱码,可以把编码格式改为GBK。
  • 直接把文件里面的全部内容用一个字符串返回,代码如下: f.read()
  • 直接把整个文本内容以一个字符串方式返回的结果
  • 这个参数除了为“w”外,还可以为“a”。它们的区别在于,如果原来已经有一个new.txt文件了,使用“w”会覆盖原来的文件,导致原来的内容丢失;而使用“a”,则会把新的内容写到原来的文件末尾。
  • CSV文件可以用Excel或者Numbers打开,得到可读性很高的表格
  • CSV文件本质上就是文本文件,但是如果直接用文本编辑器打开,可读性并不高
  • with open('result.csv', encoding='utf-8') as f: reader = csv.DictReader(f) for row in reader: print(row)
  • for循环得到的row是OrderedDict(有序字典),可以直接像普通字典那样使用
  • 这是因为f变量里面的值是一个生成器,生成器只有在被使用(更准确的说法是被迭代)的时候才会去读文本内容。但是退出with的缩进以后,文件就被Python关闭了,这个时候当然什么都读不了。
  • Python写CSV文件时需要用到csv.DictWriter()这个类。它接收两个参数:第1个参数是文件对象f;第2个参数名为fieldnames,值为字典的Key列表。

3.3 阶段案例——半自动爬虫开发

  • 手动的部分是把网页的源代码复制下来,自动的部分是通过正则表达式把其中的有效信息提取出来。

3.4 本章小结

  • 正则表达式用来在一大段文字中提取需要的内容,用得最多的组合是“(.*? )”。这个组合可以解决绝大多数的目标提取问题。

4.1 使用Python获取网页源代码

  • Python在发布时会自带一些由官方开发的常用的库,例如正则表达式“re”、时间“time”等。这些库称为“官方库”。而由非官方发布的库,则称为“第三方库”。
  • 开发者自己写的.py文件的名字绝对不能和Python自带的模块或者已经安装的第三方库的名字相同,否则会产生问题。
  • 使用浏览器来访问网页,看起来只需要输入网址就可以。但其实网页有很多种打开方式,最常见的是GET方式和POST方式。在浏览器里面可以直接通过输入网址访问的页面,就是使用了GET方式。还有一些页面,只能通过从另一个页面单击某个链接或者某个按钮以后跳过来,不能直接通过在浏览器输入网址访问,这种网页就是使用了POST方式。
  • 第3行使用.content这个属性来显示bytes型网页的源代码。第4行代码将bytes型的网页源代码解码为字符串型的源代码。
  • 有一些网页,使用GET和POST方式访问同样的网址,得到的结果是不一样的。还有另外一些网页,只能使用POST方式访问,如果使用GET方式访问,网站会直接返回错误信息。
  • 还有一些网址,提交的内容需要是JSON格式的,因此post()方法的参数需要进行一些修改

4.2 多线程爬虫

  • dummy下面有一个Pool类,它用来实现线程池。这个线程池有一个map()方法,可以让线程池里面的所有线程都“同时”执行一个函数。
  • 线程池的map()方法接收两个参数,第1个参数是函数名,第2个参数是一个列表。注意:第1个参数仅仅是函数的名字,是不能带括号的。第2个参数是一个可迭代的对象,这个可迭代对象里面的每一个元素都会被函数clac_power2()接收来作为参数。除了列表以外,元组、集合或者字典都可以作为map()的第2个参数。
  • 由于这个例子不涉及I/O操作,所以在Python GIL的影响下,使用3个线程并不会使代码的运行时间小于单线程的运行时间。
  • 多线程和事件驱动的异步模型的差异

4.4 阶段案例——小说网站爬虫开发

  • 其实Python自带处理分隔符的方法。正确的代码应该写成: 
 file_path = os.path.join(’动物农场’, ’第一章.txt')

5.1 HTML基础

  • HTML与CSS(Cascading Style Sheets,层叠样式表)、JavaScript一起构成了现代互联网的基石。

5.2 XPath

  • XPath(XML Path)是一种查询语言,它能在XML(Extensible Markup Language,可扩展标记语言)和HTML的树状结构中寻找结点。形象一点来说,XPath就是一种根据“地址”来“找人”的语言。
  • 把它们复制到Python安装文件夹下面的Lib\site-packages文件夹中即可
  • info = selector.xpath('//div[@class="useful"]/ul/li/text()')
  • 核心思想:写XPath就是写地址。
  • “倒着找地标”。也就是,从需要提取的内容往上找标签,找到一个拥有“标志性属性值”的标签为止。
  • 倒着找地标”。也就是,从需要提取的内容往上找标签,找到一个拥有“标志性属性值”的标签为止。
  • 在XPath中,属性以某些字符串开头,可以写为: 
 //标签[starts-with(@属性名,"相同的开头部分")]
  • 寻找属性值包含某些字符串的元素时,XPath的写法格式和以某些字符串开头的写法格式是相同的,只不过关键字从“starts-with”变成了“contains”。
  • 目前,lxml中的XPath不支持直接提取属性值以某些字符串结尾的情况。如果遇到这种情况,建议使用contains代替。
  • 需要注意的是,在对XPath返回的对象再次执行XPath的时候,子XPath开头不需要添加斜线,直接以标签名开始即可。
  • 注意,这里的数字是从1开始的,这和编程语言中普遍的从0开始不一样。

5.3 Beautiful Soup4

  • Beautiful Soup4在某些方面比XPath易懂,但是不如XPath简洁,而且由于它是使用Python开发的,因此速度比XPath慢。
  • 由于HTML中的class属性与Python的class关键字相同,因此为了不产生冲突,BS4规定,如果遇到要查询class的情况,使用“class_”来代替
  • find_all()返回的是BeautifulSoup Tag对象组成的列表,如果没有找到任何满足要求的标签,就会返回空列表。· find()返回的是一个BeautifulSoup Tag对象,如果有多个符合条件的HTML标签,则返回第1个对象,如果找不到就会返回None。

5.4 阶段案例——大麦网演出爬虫

  • 时间包含空格和无效的换行,可以使用字符串的.strip()方法自动清除字符串开头和末尾的无效空格和换行。

第6章 Python与数据库

  • MongoDB用来保存大量数据,Redis用于作为缓存和队列保存临时数据。

6.1 MongoDB

  • MongoDB是一款基于C++开发的开源文档数据库,数据在MongoDB中以Key-Value的形式存储,就像是Python中的字典一样
  • 数据在MongoDB中是按照“库(Database)”—“集合(Collections)”—“文档(Document)”的层级关系来存储的。如果使用Python的数据结构来做类比的话,文档相当于一个字典,集合相当于一个包含了很多字典的列表,库相当于一个大字典,大字典里面的每一个键值对都对应了一个集合,Key为集合的名字,Value就是一个集合。
  • PyMongo模块是Python对MongoDB操作的接口包,能够实现对MongoDB的增删改查及排序等操作。
  • 默认情况下,MongoDB只允许本机访问数据库。这是因为MongoDB默认没有访问密码,出于安全性的考虑,不允许外网访问。
  • MongoDB的插入操作非常简单。用到的方法为insert(参数),插入的参数就是Python的字典。
  • find_one()一次只返回一条信息。因此用得最多的是find()这个方法。
  • find()方法的第2个参数指定返回内容。这个参数是一个字典,Key就是字段的名称,Value是0或者1,0表示不返回这个字段,1表示返回这个字段。其中_id比较特殊,必须人工指定它的值为0,这样才不会返回。
  • 只有_id是一个例外,必须要指定“'_id': 0”,才不会返回,否则默认都要返回。
  • PyMongo也支持大于、小于、大于等于、小于等于、等于、不等于这类逻辑查询。
  • MongoDB支持对查询到的结果进行排序。排序的方法为sort()
  • 更新可使用update_one()和update_many()方法
  • 去重使用distinct()方法,其格式为:
  • 需要注意的是,在RoboMongo中,sort()方法接收的参数是一个字典,字典的Key为将要排序的项,Value为1或者-1。这一点是和PyMongo不同的。

6.2 Redis

  • Redis是一个基于内存的数据库,它的速度远远快过MongoDB,而且Redis比MongoDB还要简单。
  • 使用keys *可以查看当前有多少的“Key”
  • 在爬虫开发的过程中主要会用到Redis的列表与集合
  • 如果想从列表左侧读出数据,使用的关键字为“lpop”
  • lpop一次只会读最左侧的一个数据,并且在返回数据的时候会把这个数据从列表中删除
  • lrange的参数是一个闭区间,包括开始,也包括结束
  • spop也会在读了数据以后将数据从集合中删除。在爬虫的开发过程中,Redis的集合一般用于去重的操作,因此很少会把数据从里面读出来。要判断一个网址是否已经被爬虫爬过,只需要把这个网址sadd到集合中,如果返回1,表示这个网址还没有被爬过,如果返回0,表示这个网址已经被爬过了。

6.3 MongoDB的优化建议

  • 建议把要插入到MongoDB中的数据先统一放到一个列表中,等积累到一定量再一次性插入。

6.5 本章小结

  • MongoDB主要用来存放爬虫爬到的各种需要持久化保存的数据,而Redis则用来存放各种中间数据。通过减少频繁读/写MongoDB,并使用Redis来弥补MongoDB的一些不足,可以显著提高爬虫的运行效率。

7.1 异步加载

  • 像这种网页上面存在的某些文字,在源代码中却不存在的情况,绝大部分都是使用了异步加载技术。
  • JSON的全称是JavaScript Object Notation,是一种轻量级的数据交换格式。
  • 使用下面这一行代码可以将字典转换为JSON格式字符串: person_json = json.dumps(person)
  • Python的None,在JSON中会变成null; Python的True和False在JSON中会变成true和false; JSON的字符串总是使用双引号,中文在JSON中会变成Unicode码。
  • person_dict = json.loads(person_json_indent)
  • 使用异步加载技术的网站,被加载的内容是不能在源代码中找到的。
  • 这种情况称为伪装成异步加载的后端渲染。数据就在源代码里,但却不直接显示出来。
  • 这种假的异步加载页面,其处理思路一般是使用正则表达式从页面中把数据提取出来,然后直接解析
  • 对于这种多次请求才能得到数据的情况,解决办法就是逐一请求,得到返回结果以后再发起下一个请求。

7.2 请求头(Headers)

  • Headers称为请求头,浏览器可以将一些信息通过Headers传递给服务器,服务器也可以将一些信息通过Headers传递给浏览器。电商网站常常应用的Cookies就是Headers里面的一个部分。
  • 虽然对于某些网站,在请求头里面只需要设置User-Agent就可以正常访问了,但是为了保险起见,还是建议把所有项目都带上,这样可以让爬虫更“像”浏览器。

7.3 模拟浏览器

  • 有一些网站在发起AJAX请求的时候,会带上特殊的字符串用于身份验证。这种字符串称为Token。
  • Selenium需要使用WebDriver才能处理网页,这里的WebDriver可以理解为浏览器或者浏览器驱动程序。它可以是Firefox,可以是Chrome,也可以是PhantomJS
  • 为了让Selenium智能地等待网页加载完成,就需要使用“WebDriverWait”“By”和“expected_conditions”这3个关键字。
  • 特别提醒:“presence_of_element_located”的参数是一个元组,元组第0项为By.XX,第1项为具体内容。“text_to_be_present_in_element”的参数有两个:第1个参数为一个元组,元组第0项为By.xx,第1项为具体标签内容;第2个参数为部分或全部文本,又或者是一段正则表达式。

7.5 本章小结

  • 对于比较复杂的异步加载,现阶段可以先使用Selenium和ChromeDriver来直接加载网页,然后就能从被加载的网页中直接获取到需要的内容。

8.1 模拟登录

  • 模拟登录有多种实现方法,使用Selenium操作浏览器登录和使用Cookies登录虽然简单粗暴,但是有效。使用模拟提交表单登录虽然较为麻烦,但可以实现自动化。
  • Selenium不适合用于大规模的爬虫开发。
  • 对于HTTPS的网站,在requests发送请求的时候需要带上verify=False这个参数,否则爬虫会报错。

8.2 验证码

  • 有一些网站的验证码是通过单击或者拖动滑块来验证的。对于这种网站,目前最简单的办法就是使用Cookies来登录,其他方式都不好用。
  • 这就决定了在处理这个验证码识别问题的时候,一定要使用Session模块,绝对不能使用requests.get()直接访问,否则代码量会增加不少。
  • open()的第2个参数“wb”表示以二进制方式写文件。
  • 开源的OCR库pytesseract配合图像识别引擎tesseract,可以用来将图片中的文字转换为文本。
  • 在线验证码识别的网站,简称打码网站。

9.1 数据抓包

  • 所谓抓包(Package Capture),简单来说,就是在网络数据传输的过程中对数据包进行截获、查看、修改或转发的过程。如果把网络上发送与接收的数据包理解为快递包裹,那么在快递运输的过程中查看里面的内容,这就是抓包。
  • 在Charles启动时,系统自带浏览器的所有HTTP流量都会经过Charles,此时可以看到数据包
  • 计算机上的任意软件,如果支持自定义代理的功能,那么设置代理IP为127.0.0.1,端口为8888,也可以让Charles监控这个软件。
  • 为了搜索到中文,需要首先在Python里面创建一个带中文的字典或者列表,然后把它转换成JSON,再从JSON中复制出中文对应的Unicode码来进行搜索
  • 使用Charles,可以轻松截获手机App和微信小程序的数据包,从而开发出直接抓取App后台和小程序后台的爬虫。
  • Charles只能截获HTTP和HTTPS的数据包,如果网站使用的是websocket或者是flashsocket,那么Charles就无能为力。

9.2 中间人爬虫

  • 中间人(Man-in-the-Middle, MITM)攻击是指攻击者与通信的两端分别创建独立的联系,并交换其所收到的数据,使通信的两端认为其正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。在中间人攻击中,攻击者可以拦截通信双方的通话,并插入新的内容或者修改原有内容。
  • mitmproxy的强大之处在于它还自带一个mitmdump命令。这个命令可以用来运行符合一定规则的Python脚本,并在Python脚本里面直接操作HTTP和HTTPS的请求,以及返回的数据包。
  • 这是由于mitmdump的脚本对第三方库的支持有缺陷,很多第三方库都不能运行,甚至Python自带的写文件功能都不能运行。
  • 由于Windows自带的CMD是没有管道这种强大的工具的,因此Windows是没有办法直接完成这个流程的。

10.1 实现原理

  • UiAutomator是Google官方提供的Android自动化图形接口测试框架。通过它可以实现对Android设备屏幕的各种操作,或者直接从屏幕上读取文字。
  • 使用watcher可以让程序在找不到元素时自动尝试解决问题

11.2 Scrapy的使用

  • 需要特别强调的是,Scrapy的爬虫绝对不能通过Python直接运行example.py来运行。
  • 当不需要从抓取到的信息中提取文本的时候,就不需要调用extract()方法。
  • 其中对于开发Scrapy爬虫来说,需要关心的内容如下。(1)spiders文件夹:存放爬虫文件的文件夹。(2)items.py:定义需要抓取的数据。(3)pipelines.py:负责数据抓取以后的处理工作。(4)settings.py:爬虫的各种配置信息。
  • items.py文件用于定义需要爬取哪些内容。每个内容都是一个Field

13.1 法律问题

  • 如果目标网站本身就是提供公众查询服务的网站,那么使用爬虫是合法合规的。但是尽量不要爬取域名包含.gov的网站。

13.2 道德协议

  • 在Scrapy工程的settings.py文件中,有一个配置项为“ROBOTSTXT_OBEY”,如果设置为True,那么Scrapy就会自动跳过网站不允许爬取的内容。