1. 正则表达式
1.1 基本符号
| 符号 | 含义 |
|---|---|
| . | 一个点号可以代替除了换行符以外的任何一个字符,包括但不限于英文字母、数字、汉字、英文标点符号和中文标点符号 |
| * | 一个星号可以表示它前面的一个子表达式(普通字符、另一个或几个正则表达式符号)0次到无限次。 |
| ? | 问号表示它前面的子表达式0次或者1次。注意,这里的问号是英文问号。 |
| .*? | 问号最大的用处是与点号和星号配合起来使用,构成“.*? ”。通过正则表达式来提取信息的时候,是用到最多的组合。 |
| .* 和.*? | . * 是贪婪匹配,获取最长的满足条件的字符串。.*?是非贪婪,匹配一个能满足要求的最短字符串。 |
| \ | 反斜杠需要和其他的字符配合使用来把特殊符号变成普通符号,把普通符号变成特殊符号。在使用了反斜杠以后,反斜杠和它后面的一个字符构成一个整体,因此应该将“\n”看成一个字符,而不是两个字符。 |
| \d | 正则表达式里面使用“\d”来表示一位数字。为什么要用字母d呢?因为d是英文“digital(数字)”的首字母。 |
| () | 小括号可以把括号里面的内容提取出来。 |
1.2 re模块
| 语法 | 用法 |
|---|---|
| re.findall(pattern, string,flags=0) | pattern是正则表达式,string是待处理的字符串,flags是一些特殊功能的标志。findall即以列表的形式返回所有满足要求的字符串。 |
| re.search(pattern, string,flags=0) | search()的用法和findall()的用法一样,但是search()只会返回第1个满足要求的字符串。 |
| re.match(pattern, string, flags=0) | 从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。 |
| re.compile(pattern[, flags]) | 用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。 |
| re.sub(pattern, repl, string, count=0, flags=0) | pattern是正则表达式。 repl是替换的字符串,也可为一个函数。 string : 要被查找替换的原始字符串。 count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。 |
| re.split(pattern, string[, maxsplit=0, flags=0]) | split 方法按照能够匹配的子串将字符串分割后返回列表。 |
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
2. 获取网页内容
2.1 requests
相信你入门爬虫,一定会看到很多模块,明白谁和谁是相同的效果,并且两者中谁能更便利得到达这个相同的效果,模块urllib和urllib2与第三方库requests就是这样的关系,都能通过请求url,获取网页源代码。
1 | # 使用requests库获取网页源代码 |
2.2 BeautifulSoup
使用BeautifulSoup的两个经典步骤:
(1)处理源代码生成BeautifulSoup对象。获取网页源代码后,将html以’lxml’的格式“喂”给bs4模块的BeautifulSoup,做成美味的汤汁吧。
(2)使用find_all()或者find()来查找内容。 find()与find_all()的不同点如下。find_all()返回的是BeautifulSoup Tag对象组成的列表,如果没有找到任何满足要求的标签,就会返回空列表。find()返回的是一个BeautifulSoup Tag对象,如果有多个符合条件的HTML标签,则返回第1个对象,如果找不到就会返回None。
除了获取标签里面的文本外,BS4也可以获取标签里面的属性值。如果想获取某个属性值,可以将BeautifulSoup Tag对象看成字典,将属性名当作Key,比如find(‘div’)[‘class’]返回的结果为列表。
1 | from bs4 import BeautifulSoup |
3. 多线程爬虫
单线程爬虫只有一个进程、一个线程。单线程爬虫每次只访问一个页面,不能充分利用计算机的网络带宽。
Python的全局解释器锁(GlobalInterpreter Lock, GIL)导致Python的多线程都是伪多线程,即本质上还是一个线程,微观上的单线程,在宏观上就像同时在做几件事。这种机制在I/O(Input/Output,输入/输出)密集型的操作上影响不大,但是在CPU计算密集型的操作上面,由于只能使用CPU的一个核,就会对性能产生非常大的影响。所以涉及计算密集型的程序,就需要使用多进程,Python的多进程不受GIL的影响。爬虫属于I/O密集型的程序,所以使用多线程可以大大提高爬取效率。
Python的多进程库multiprocessing,用来处理与多进程相关的操作。但是由于进程与进程之间不能直接共享内存和堆栈资源,而且启动新的进程开销也比线程大得多,使用多线程来爬取比使用多进程有更多的优势。
multiprocessing下面有一个dummy模块,它可以让Python的线程使用multiprocessing的各种方法。dummy下面的Pool类,用来实现线程池。这个线程池有一个map()方法,可以让线程池里面的所有线程都“同时”执行一个函数。
线程池的map()方法接收两个参数,第1个参数是函数名,第2个参数是一个列表。注意:第1个参数仅仅是函数的名字,是不能带括号的。第2个参数是一个可迭代的对象,这个可迭代对象里面的每一个元素都会被函数clac_power2()接收来作为参数。除了列表以外,元组、集合或者字典都可以作为map()的第2个参数。
1 | import multiprocessing as mp |
可以看到适当的线程个数会让程序执行时间缩短。