第1章 开发环境配置
第2章 爬虫基础
-
HTTP基础
-
URI和URL:分别表示统一资源标识符和定位符,URL是URI的子集。
-
HTTP和HTTPS:HTTP是超文本传输协议,而HTTPS是加入SSL层后的更安全的HTTP。
-
HTTP请求过程:请求的过程是从客户端向服务器端发送一个请求,服务器接收到请求再把相应的响应发送回去,这样就完成了一个请求过程。
这里通过浏览器的开发者工具可以监听页面请求,这个对于爬虫来说应该是一个比较重要的分析过程吧。
Network面板中:
- Name:请求的文件名
- Status:响应状态码
- Type:请求的文档类型
- Initator:请求源
- Size:资源大小
- Time:发起请求到获取响应所用时间
- Waterfall:网络请求瀑布流
点击请求有更具体的信息为:
- Request URL:请求的完整URL
- Request Method:请求的方式(GET、POST等)
- Status Code:响应状态码
- Remote Address:远程服务器地址和端口
- Referer Policy:Referer判别策略
以上这些要明白其意义及作用。
-
请求方法:
方法 描述 GET 请求指定页面信息,并返回页面内容 HEAD 与GET请求类似,但返回的响应信息没有具体内容,仅用于获取响应头 POST 请求指定页面信息,提交的数据包含在请求体中,常用于表单提交或上传文件等 。。。 。。。 -
请求头:
字段 描述 Accept 请求报头域,用于告诉服务端,客户端可以接收什么类型的信息 Accept-Charset 指定客户端可接受的编码格式 Content-Type 请求体的文档类型 Cookie 存储在本地的数据 Host 服务端的域名及端口 Referer 标识请求从哪个页面发过来的,可以做来源统计、防盗链等 User-Agent 用于表示客户端的操作系统和浏览器版本 请求头是编写爬虫的重要步骤,必须理解。
-
响应状态码:
类型 含义 1 信息,服务器接收到请求,需要请求者继续执行操作 2 成功,操作被成功接收并处理 3 重定向,需要进一步操作以完成请求 4 客户端错误,请求包含语法错误或无法完成请求 5 服务器错误,服务器在处理请求时发生错误 熟记响应状态码的意义可以很好的了解爬虫的运行状况,以便准确的作出应对措施。
-
响应头:
字段 描述 Content-Type 响应体中数据的类型 Content-Encoding 响应内容的编码 Server 包含服务器的信息,如服务器名、版本号等 Set-Cookie 设置Cookie
-
-
网页基础
-
HTML:超文本标记语言,整个网页就是由各种节点嵌套组合而成的,有着复杂的层次关系。
-
CSS:层叠样式表,主要就是把页面节点布局到合适的位置,相当于是装饰性的东西吧。
-
CSS选择器:这个还是需要了解一下的,有时候爬虫需要用CSS选择器来定位元素。以下列出一些常用的:
选择器 例子 说明 .class .title 选择class为title的所有节点 #id #button1 选择id为button1的所有节点 [attribute] [target] 选择带有target属性的所有节点 还有很多只能日后要用再了解了。
-
JavaScript:一种脚本语言,可以让页面有动态效果。爬虫中也常遇到,需要解析JS代码获取其中的信息。
-
-
静态页面和动态页面
静态页面是用HTML编写的,内容固定。优点是加载速度快、编写简单,缺点是维护性差、不能根据条件动态产生页面内容。
动态页面是根据不容条件显示不同内容,实现方式很多,都是由服务端根据不同条件动态想客户端发送内容。
应该说静态页面会比较好爬内容吧,动态难一点。
第3章 网络库 urllib
第4章 urllib3
第5章 requests
-
基本用法
- 用get向页面发送请求:requests(url, params=None)
- 添加HTTP请求头:在get请求中添加headers的字典参数,一般会有user-agent、cookie等
- 抓取二进制数据:请求的内容是二进制文件,如png图片、pdf文档等等,这些需要保存为文件,可以使用content属性得到bayes形式的数据,然后设置后缀保存文件
- post请求:发送POST请求要指定data字典参数,如requests.post(url, data=data)
- 响应数据:返回的响应是一个Response对象,含有很多信息,如状态码、响应头、Cookie等
-
高级用法
-
上传文件:实际上是一个POST请求,只需指定files参数即可,可以是BufferedReader对象,这个是python中用open方法打开文件的一个对象
-
处理Cookie:设置Cookie有两种方法:一是创建RequestsCookieJar对象,然后用set方法设置每一个Cookie,这个方法是太麻烦了,所以直接忽略;二是Cookie包含在headers字典中
-
使用同一个Session(会话):这个应该也重要吧,要让爬虫作为同一个客户端多次抓取页面,就要保持在同一个Session下,requests提供了Session对象,大致的用法如下:
session = requests.Session() session.get(url) r2 = session.get(url) ...
这个目前用的也很少,只能说先了解了。
-
SSL证书验证:用requests请求HTTPS URL时,如果证书验证错误就会报一个SSL的错误。这是requests默认会对证书进行验证。有一个默认参数verify值为True,可以设为False来跳过验证
-
使用代理:requests使用代理发送请求只需指定proxies字典参数。
proxies = {'http': 'http://123.123.11.12:80', 'https': 'https://123.123.11.12.80'}
然后有的代理要使用什么HTTP Basic Auth,就要用类似
http://user:password@host:port
这样的语法设置。proxies = {'http': 'http://user:password@123.123.11.12:80'}
Request还支持SOCKS协议的代理,要用的话需要安装第三方库,
pip install requests[socks]
。不过我没试成功,还是得等有代理再看情况了。 -
超时:爬虫过程中常遇到的就是超时问题了,一个做法就是用
try...except
语句捕捉异常,另一个就是指定timeout参数。 -
身份验证:用requests进行身份验证只需设置auth参数,auth参数的值是一个HTTPBasicAuth对象,封装了用户名和密码。不过这个我还真没用过。
-
第6章 Twisted网络框架
第7章 正则表达式
- 用match方法匹配字符串:re.match(pattern, str)用于在字符串str中匹配模式pattern,匹配成功会返回一个对象,失败为None。
- 用search方法查找模式:使用与match类似,只不过是可以在字符串中找到多个与模式匹配的字符串。
- 匹配多个字符串:要搜索多个字符串可以利用正则表达式的知识,用|符号,可以理解为“或”。
- 匹配任何单个字符:这个就是模糊匹配了,这个要记的就多了,有很多符号表示什么意思之类的,比如 . 表示匹配任意单个字符,*表示前一个字符出现任意次。
- 分组:一个模式字符串中用圆括号括起来的部分一般是我们需要的,这部分就会作为一组,然后通过group的方法获取指定的组匹配的字符串,也是挺方便的。
- 使用findall和finditer查找每一次出现的位置:返回一个能匹配模式的所有字符串的列表,和search有点类似。
- 使用sub和subn搜索与替换:这两个方法用来搜索和替换,替换匹配出来的字符串。
- 使用split分割字符串:根据正则表达式分割字符串,这个挺好用的,对于一些复杂形式的字符串,只要正则表达式写的好就能很好的分割出来。
最后,虽然觉得正则表达式挺强大的,但是使用起来终究还是感觉复杂,表达式不好写,容易出错,所以通常都是使用替代品,比如BeautifulSoup等其他好用的工具。
第8章 lxml与XPath
-
lxml基础
lxml是一个解析HTML和XML的解析库,支持XPath的解析方式。
lxml的操作有:
- 使用parse函数传入XML文件来读取XML文件,返回一个对象,可以获取某个节点等
- 用fromstring函数解析字符串形式的XML文档
- 操作HTML,也类似于操作XML。由lxml导入etree模块,有方法HTMLParser(),然后获取节点。
-
XPath
XPath的基本语法是多级目录。
描述 nodename 选取此节点的所有子节点 / 从当前节点选取直接子节点 // 从当前节点选取子孙节点 . 选取当前节点 .. 选取当前节点的父节点 @ 选取属性 用法比如:tree = etree.HTML(html), tags = tree.xpath("...")
选取所有节点:"//*"
选取子节点:一个斜杠,表示选取当前节点下的直接子节点
属性匹配与获取:获取节点的属性用@,然后可以加[]中括号表示过滤条件
多属性匹配:可以使用and和or表示与和或逻辑
第9章 Beautiful Soup库
Beautiful Soup库是一个非常非常方便的XML和HTML解析库,还记得刚入门爬虫的时候就接触到了这个库,用起来也很好入手,几个易懂的语句就能从页面中提取需要的数据。还是继续从书中学习这个库的知识吧。
-
Beautiful Soup基础
第一步是选择解析器,它支持多种解析器,比如lxml等。我也只了解python标准库的HTML解析器和lxml的HTML、XML解析器其他的都不熟。这里也再记一记他们的优缺点吧:
解析器 使用方法 优点 缺点 Python标准库 BeautifulSoup(code, 'html.parser') 执行速度适中,容错能力强 lxml HTML解析器 BeautifulSoup(code, 'lxml') 解析速度快,容错能力强 需要安装C语言库 lxml XML解析器 BeautifulSoup(code, 'xml') 解析速度快,唯一支持XML的解析器 需要安装C语言库 可以看出,基本上爬虫中lxml解析器是最佳的选择了,事实上这个库的使用我也只用过这个解析器,所以其他的解析器没有什么感觉。
-
节点选择器
- 获取节点的名称:soup.title.name
- 获取节点的属性:soup.li.attrs,是一个字典,后面还可以获取具体的值
- 获取节点内容soup.a.string,用string属性获取节点包含的文本内容
- 选择子节点:soup.head.children
- 选择父节点:soup.a.parent
-
方法选择器
- find_all方法:根据节点、属性、文本内容等选择所有符合要求的节点。要注意的是找class属性时要后面加下划线(class_)。
- find方法:只查询满足条件的第1个节点,其他和find_all用法一样。
-
CSS选择器
BeautifulSoup还有一种CSS选择器,CSS虽然知道,但这个CSS选择器就了解甚少了。
-
基本用法:
用select方法,接收字符串类型的CSS选择器
(1).classname:选取样式名为classname的节点,是选取class属性值的节点
(2)nodename:选取节点名为nodename的节点
(3)#idname:选取id属性值为idname的节点
-
嵌套选择节点
这个和节点选择器类似,都是可以嵌套调用。这个也不难理解,HTML代码有很多节点构成,而节点中又会有其他各种节点,所以从不同角度看都是有着嵌套的节点
-
获取属性值与文本
获取属性值用attrs,也可以直接用[...]方式引用节点的属性。获取节点的文本内容用get_text()方法,也可以使用string属性。
-
最后就是小结一下:Beautiful Soup简直太方便了,结合浏览器本身可以获取对应节点的CSS、XPath,基本上想从页面中获取什么内容都能很快获取,当然前提是能够获取到想要的源代码。而爬虫远不止这获取源代码这一部分的内容。
第10章 pyquery库
第11章 文件存储
这一章主要讲有关文件的操作,因为爬虫爬取到的数据要进行分析,就得存储到文件中去,而各类文件的操作及操作方法都有所不同,还要掌握一些工具。
-
打开文件
用open函数打开一个文件,返回一个对象,这个返回的东西感觉类似于C语言中的指针吧,可以用来操作文件内容。而打开文件的模式有'r'、'w'、'a'、'b'等等,这些含义都需要了解,选择对应需要的模式。
打开文件后的返回值有几个方法:
- write(string):向文件写入内容,会返回写入文件的字节数
- read([n]):读取文件内容,n是可选参数,表示读取文件指针位置之后的n个字节,否则就是之后的全部字节
- seek(n):重设文件指针。这里如果用write写入内容后,文件指针应该是在文件末尾的,所以如果需要再读取内容需要调用seek(0)才能读取刚才写入的内容
- close():关闭文件,对文件进行读写操作后要记得关闭,这个貌似也挺像C语言中的一样,不关闭就相当于还占用着这个文件,其他地方就可能打不开这个文件。当然,好像程序结束后都会自动关闭的,只是这样记得关闭是一个好习惯。而且python中还有上下文管理器with,这个也能用open打开文件,而且结束后会自动关闭文件,可读性也好,就挺不错的。
-
读行和写行
用read和write加上行结束符也可以读写文件的整行,但比较麻烦。而有readline、readlines和writelines这些用于读写整行的方法(注意没有writeline方法)。
-
用FileInput对象读取文件
按照书上的讲解:如果读取一个非常大的文件,使用readlines函数会占用很多内存,而一个解决方法就是用循环加readline方法逐行读取。其次就是用fileinput模块中的input函数读取指定的文件。
还了解到,input方法返回一个FileInput对象,这个对象使用缓存机制,不会一次性读取文件所有内容,所以比readlines更节省内存资源。然后用法也简单,语句是fobj = fileinput.input(filepath)
-
处理XML格式的数据
因为这个格式的数据接触的很少,而且也好少用到过,所以就快速浏览过去了。
- 读取与搜索XML文件:Python中导入XML模块利用其中的API操作XML文件,比如xml.etree.ElementTree模块。
- 字典与XML字符串的转换:可以利用dicttoxml、xmltodict模块
-
处理JSON格式的数据
这个就很广泛了,而且也有很多优点
-
JSON字符串与字典相互转换:用json模块的dumps和loads方法,并且要注意loads是JSON转字典,而load是从文件中的JSON转字典,这个搞混淆了就怪尴尬的。还有就是有时候会遇到转换错误,我遇到的时候是有一个引号的问题,字符串中的引号需要是双引号才能转为字典,这个时候还可以利用eval()方法来执行代码返回字符串对应的字典
-
将JSON字符串转换为类实例
这个特性没用过,不过也还能看懂。不同于普通的loads方法,传入object_hook关键字参数,指定一个类或回调函数,则可以将JSON字符串转换为类实例。
-
JSON字符串转XML字符串:借助工具:dicttoxml以及dicttoxml
-
-
CSV文件存储
CSV文件是叫做逗号分隔值文件,这个文件可以用Excel打开并编辑,而又比xls类的文件更简洁,结构清晰简单,所以用CSV保存数据是比较方便的。
- 读写CSV文件:Python提供csv模块,不过我没有用过,并且看起来没有那么便捷。一般我都是使用pandas的read_csv和to_csv方法来对CSV文件进行读写,只要一个语句,非常的方便,基本上满足了所有的需要了。
-
小结
这章讲了很多种读写文本文件的方式,这里的文本文件不只是txt格式的,包括XML、JSON和CSV都算作了纯文本的文件,只是文件的数据组织形式不同。而JSON和CSV文件最常用,因为这两种文件数据冗余较小,适合通过网络传输。然后,数据存储当然不仅仅只有文本文件,还有各种数据库,比如SQLite、MySQL、Mongodb等,这也是下一章的内容了。
第12章 数据库存储
-
SQLite数据库
- 管理SQLite数据库:一个跨平台的SQLite数据库管理工具:DB Browser for SQLite
- 用Python操作SQLite数据库:sqlite3模块提供的函数可以操作SQLite数据库。一般先要用connect方法打开SQLite数据库,然后用cursor方法获取sqlite3.Cursor对象,通过这个对象的execute方法执行各种SQL语句,增删改查等等。
-
MySQL数据库
-
安装:要了解MySQL的安装,登录操作以及数据库创建等基本知识。
-
在Python中使用MySQL:用pymysql模块,记住几个函数和方法:
(1)connect函数:连接数据库
(2)cursor方法:获取操作数据库的Cursor对象
(3)execute方法:用于执行SQL语句
(4)commit方法:在修改数据库后,需要调用该方法提交修改
(5)rollback方法:数据库回滚
-
-
非关系型数据库
前面的两种数据库都是关系型数据库,数据以二维表的形式存储。另一种是非关系型数据库,叫NoSQL,这个就了解的太少了,需要好好了解一下。
非关系型数据库主要包括对象数据库、键-值数据库、文档数据库、图形数据库、表格数据库等。书中主要是要介绍很流行的文档数据库MongoDB。
-
MongoDB数据库
书中讲了一个非常通俗的例子:如果要保存博客和相关的评论,用关系型数据库就需要建立两个表,一个保存博文,另一个保存评论,然后通过键值关联这两个表,这样如果有更多的数据,关系也会更复杂,需要关联更多的表;而如果使用MongoDB,就可以直接把博文和评论都放在一个文档中存储,无须关联,查询速度也更快。这里就让我明白了一些有关非关系型数据库的特点。
-
pymongo模块
Python中用pymongo模块可以操作MongoDB数据库。然后具体的操作方式就要结合文档实操了。
然后就是可以结合爬虫,把爬取到的数据放进数据库中,这样更能了解数据库,操作也会越来越熟练,这对数据处理也有很大的裨益。
-
第13章 抓取异步数据
-
异步加载与AJAX
一些网页是静态的,意味着内容是通过服务器端以同步的方式按顺序发送到客户端的,这种网页应该都比较好爬。而一些页面使用了异步加载的方式,就是让静态部分(HTML、CSS、JS等)先以同步的方式装载,然后动态的部分再另外向服务器发送一个或多个异步请求,然后再显示,这种叫AJAX。虽然具体原理是不清楚的,但是在爬虫中却是常常遇到动态页面,普通的方式就不能获取到含有真实数据的源码。
-
基本原理
AJAX的实现分为3步:发送请求->解析响应->渲染页面。
(1)发送请求
在页面中实现业务逻辑与页面交互的是JS,而发送请求需要调用浏览器提供的API,然后还有什么jQuery,这些就不深入了解了
(2)解析响应
主要是响应JSON格式的数据
(3)渲染页面
渲染主要是将从服务器端获取的响应数据以某种形式显示在Web页面的某些元素上
-
逆向工程
前面学了AJAX请求、异步加载,而这些会让爬虫增加困难,就是要知道页面的数据是怎么加载进去的,要怎么获取异步请求的URL,没想到的是这个也能叫逆向工程,以前总是以为破解软件、源码之类的才会这么说,居然这个过程也称为逆向工程,瞬间就感觉高大尚了。
书中给了一个例子,里面说明了一个问题,有时获取到的页面源码可能是在JS渲染页面前,可以在开发工具的Response选项看到,而Elements选项卡显示的是JS渲染后的。我们需要的是渲染后的,因为这才是页面最后显示的样子,需要的数据也在这里,而用requests抓取的HTML代码和Response选项卡中显示的HTML代码是一样的。所以,如果数据没有在Response选项卡中,那么很可能是通过异步方式获取的数据,然后再利用JS将数据显示在页面上。
而目的就是找到异步访问的URL。有几种方式可以了解,如果知道大概的URL名字,可以利用开发者工具中的Filter文本框过滤,不知道就使用XHR过滤,然后就在里面找返回需要数据的那条请求。
最后就是提取数据,一般数据格式是JSON,可以用json模块的loads转换为字典。
第14章 可见即可爬
从之前可以知道从Web页面获取数据的方式有同步和异步(AJAX)两种,对于同步的方式比较简单,而异步需要分析异步请求的URL。还有一类页面是通过JS在前端得到的,要想找到规律,就需要分析JS代码。但是很多参数都是加密的,比较难发现规律,给爬虫造成了困难。那么selenium就是刚接触爬虫时就了解到的可见即可爬的工具,用它获取的页面源码是和正常浏览看到的是一样的,所以不管页面多复杂都能获取渲染后的页面源码。
- 第一步是需要安装一系列的东西,比如selenium模块、chromedriver等。
- selenium基本使用:
browser = webdriver.Chrome(),browser.get(url)
还有一些获取页面元素的方法。 - 查找节点:
find_element_by_
方法有很多,命名是有规律的,但是以我以前用过的经验来看,有的页面获取元素是能够获取到的,但是有的就获取不到,为空,尽管代码看起来没什么错误。 - 节点交互:selenium还可以模拟浏览器的动作,比如单机某个按钮、输入文本等。
- 动作链:还有一些页面需要鼠标拖曳、按键等,这些可以用ActionChains对象实现。
- 执行JS代码:对于某些操作selenium没有提供相应的API,但可以用selenium的execute_script方法直接运行JS代码实现。
- 管理Cookies:用selenium可以方便地管理Cookie,例如获取、添加、删除Cookie等。
- 改变节点属性值:selenium本身并没有提供修改节点属性的API,不过可以通过执行JS代码的方式设置节点属性。
小结:selenium可以说是同时跨测试和爬虫两个领域,在很多页面中,selenium比requests、beautifulsoup等库方便很多,但是是速度更慢,因为需要模拟浏览器的动作和页面渲染需要时间,所以可以根据需要选择合适的方法。
第15章 基于Splash的爬虫
Splash是一个JS渲染服务,是一个带有HTTP API的轻量浏览器。功能有很多,但是之前未曾了解过,所以很多都看不懂,就挑几点来说吧,一个能够异步处理多个网页的渲染,可以获取渲染后的页面源代码或截图,还可以用JS处理网页内容。基于这几点看来好像挺不错。
了解完是什么,之后就是如何安装,这个跟着说明、教程走就行了。
然后就是介绍了splash lua脚本,还有一系列关于页面渲染的操作、许多关于爬虫的方法。
抓取数据有多种方式,selenium和splash是其中两种,也就是通过模拟浏览器的动作来抓取数据,这种方式的好处就是不需要过多对页面获取数据的方式进行分析,只看最后的结果即可。但区别是,selenium是纯python编程就行,而splash要结合Lua脚本,对于不熟悉它的我来说就比较困难了,但也看情况去学吧。
第16章 抓取移动APP的数据
之前学的内容都是关于抓取Web页面的,而如果要抓取移动APP的数据,则又有不一样的方式了。由于移动APP通常都会采用异步的方式从服务端获取数据,所以就需要分析移动APP用于获取数据的URL,然后才可以用requests等库抓取数据,听起来和AJAX挺像的。
但是移动APP又不能像PC浏览器上有开发者工具让你进行分析,所以要想监控APP发送的请求,就需要用一些抓包工具,这个有很多,但是我了解的多的就是Fiddler,所以就选择用它了,不过书中用的是Charles。
大致的内容有:
- 抓取HTTP数据包:主要就是设置方面要弄好
- 安装双端证书:要监听数据必须要在两端都安装好证书
- 监听HTTPS数据包
其次就是还有其他很多的工具,具体使用就看情况选择。
第17章 使用Appium在移动端抓取数据
第18章 多线程和多进程爬虫
有时候写爬虫只是为了需要一部分数据,只需要一个程序就能爬取到需要的数据,这种情况可以很快的获取到需要的数据。但是一些时候需要爬取相对大量的数据,这样爬虫内容就很多,花费的时间也很多。之前用selenium爬取网易云的数据时就充分感受到了这一点,在本地运行爬虫真的要花很多时间,效率很低。所以,用多线程、多进程来运用到爬虫上效率就会大大提高。这个学会了应该是非常有用的。
-
线程与进程
- 进程:计算机程序有静态和动态的区别,静态的就是存储在磁盘上的可执行二进制文件,动态的就是将这些文件加载到内存中并被操作系统调用,这个动态的程序被称为一个进程。光看这里也许能理解,但还是挺抽象的。只有可执行程序被调入内存才叫进程,每个进程都有自己的地址空间等数据。
- 线程:线程与进程类似,不过线程是在同一个进程下执行的,线程必须依赖进程才能执行。线程包括开始、执行、结束三个部分。一个进程中的各个线程与主线程共享同一块数据空间,所以线程间的信息共享和通信更容易。线程一般是以并发方式执行的,所以可以进行多任务间的协作。但是多线程之间共享数据可能会导致结果不一致,因为不同线程访问同一块数据会由于访问顺序不同而操作结果不一样。不过一般线程库都会有方法让共享的内存区域数据同步。
有关线程、进程的知识有很多,一开始会觉得很难懂,但是多理解和动手会慢慢的理解这两个到底是什么的。
-
Python与线程
-
使用单线程执行程序
主要是和多线程进行比较,很明显的地方就是运行时间,多线程的效率更高。一般的程序都是单线程,比如运行两个函数,单线程就是需要一个一个运行,第一个运行时就阻塞了,运行完毕才开始第二个;而多线程则可以在运行第一个的时候,几乎同时开始运行第二个,意思是没有完全的同时开始两个函数,只是速度比较快的切换线程去执行函数,这是我的理解。
-
使用多线程执行程序
Python中有很多内建模块用于支持多线程,书中讲了第一个是_thread。使用_thread模块中的start_new_thread函数会直接开启一个线程,第一个参数是一个函数,可以把这个函数称为线程函数,当线程启动时会自动调用这个函数;第二个参数是给线程函数传递的参数,必须为元组类型。可以写两个简单的函数验证多线程的运行结果。
-
为线程函数传递参数
就是在start_new_thread的第二个参数传入元组类型。这里关心的地方是,启动线程后,如果不暂停,会出现没等线程函数运行,程序就结束了,这里是还不太理解的地方。
-
线程和锁
之前的多线程中运行线程函数,最后都需要使用sleep函数让程序处于休眠状态,或使用input函数从终端收集一个字符串,目的是让程序暂停,在所有线程函数都执行完之前阻止程序退出。因为程序无法感知是否有线程正在执行,以及是否所有线程函数都执行完毕。所以,这样看起来挺麻烦的。
于是,要了解什么是锁,这里的锁不是把程序锁住,不退出,而是可以通过锁让程序了解是否还有线程函数没执行完毕,而且可以做到当所有线程函数执行完后,程序会立刻退出,不需要任何等待。
锁的使用分为创建锁、获取锁和释放锁。这三个功能需要_thread模块中的1个函数和2个方法,allocate_lock函数用于创建锁对象,然后使用锁对象的acquire方法获取锁,如果不需要锁了,可以使用锁对象的release方法释放锁。locked方法可以判断锁是否被释放。
-
threading模块
python常用的多线程模块有threading和Queue,这里学一下threading。这个模块的Thread类是主要的对象,可以来创建线程,可以创建Thread的实例,也可以派生子类,创建子类的实例。
大致的步骤如图。多个线程就多个运行。
有时也经常见别人是通过继承Thread类,派生一个子类进行创建多线程。这样对于获取线程函数中的返回值也方便。
下面列出了threading模块的类与函数,有助于理解这个模块:
-
线程同步
线程同步也是一个重要的问题,从中可以了解线程同步的机制。
首先是线程锁(互斥锁)。在多进程中,每一个进程都拷贝了一份数据,而多线程的各个线程则共享相同的数据。所以多线程占用的资源更少,但是共享数据就可能导致结果不一致,这是之前就了解到的。解决方法是线程锁,大概就是函数需要获得锁才能运行,运行完再释放锁,同一个锁同一时间只能被一个线程使用。这样的话,虽然能解决变量混用造成的数据结果不一致的问题,但是也降低了运行效率,因为一个线程使用锁运行时其他线程无法—起运行,而且使用锁一定要小心,可能会造成死锁或是其他的问题。
其次是GIL锁。GIL锁全称是全局解释锁(Global Interpreter Lock),任何python线程在执行前都要先获得GIL锁,然后每执行一部分代码,解释器就会自动释放GIL锁,其他线程就可以竞争这个锁,只有得到才能执行程序。
-
第19章 网络爬虫框架:Scrapy
Scrapy是一个快速、高层次的屏幕抓取和Web抓取框架,用于抓取Web站点并从页面中提取出结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试等。因为它的内容太多了,所以挑了一部分写下来。从基础理解,主要是了解框架的组件和框架的运行过程。
-
框架包含的组件有:
- 爬虫中间件:位于Scrapy引擎和爬虫之间的框架,主要是用于处理爬虫的响应输入和请求输出。
- 调度器中间件:位于Scrapy引擎和调度器之间的框架,主要用于处理从Scrapy引擎发送到调度器的请求和响应。
- 调度器:用来接收引擎发过来的请求,压入队列中,并在引擎再次请求的时候返回。像是一个URL的优先队列,决定了下一个要抓取的网址是什么,同时在这里会去除重复的网址。
- 下载器中间件:位于Scrapy引擎和下载器之间的框架,主要用于处理Scrapy引擎与下载器之间的请求及响应。代理IP和用户代理可以在这里设置。
- 下载器:就是下载网页内容,并将网页内容返回给爬虫。
- Scrapy引擎:用来控制整个系统的数据处理流程,并进行事务处理的触发。
- 爬虫:爬虫就是具体的爬虫程序了,用于从网页中提取自己需要的信息。也可以从中提取URL,让Scrapy继续爬取下一个页面。
- 项目管道:负责处理爬虫从网页中爬取的项目,主要的功能就是持久化项目、验证项目的有效性、清除不需要的信息。当页面被爬虫解析后,将被送到项目管道,并经过几个特定的次序来处理其数据。
-
框架的运行过程:scrapy引擎从调度器中取出一个URL用于接下来的抓取;引擎把URL封装成一个请求传给下载器;下载器把资源下载下来,并封装成一个响应;然后爬虫解析响应;解析出的是项目,就交给项目管道进行下一步处理;解析出的是链接URL,就把URL交给调度器进行下一步的抓取。
这个框架还是挺强大的,内容很多,比如还要了解框架的数据流向、配置等。以上只是列出了部分内容,更多还需要去一步步掌握。
评论区