正则表达式
用来处理字符串--实现字符串的检索、替换、匹配验证 URL: [a-zA-z]+://[^\s]* a-z代表匹配任意小写字母,\s表示匹配任意的空白符号,*代表匹配匹配前面的字符任意多个 正则表达式不是py独有,但py的re库提供了整个正则表达式的实现
-
match() 向他传入要匹配的字符串以及正则表达式,就可以检测这个正则表达式是否匹配字符串了 从字符串起始位置往后找,匹配则返回成功的结果,否则返回None
import re content = 'Hello 123 4567 World_This is a Regex Demo' print(len(content)) result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}',content) print(result) # 匹配成功,返回SRE_Match对象 print(result.group()) # group()方法可以输出匹配到的内容 print(result.span()) # span()方法输出匹配范围--(0,25) # 开头的^是匹配字符串开头,即以Hello开头,3个\d匹配3个数字,\d{4} 表示匹配4个数字,\w{10}表示匹配10个字母及下划线-
匹配目标 这里可以使用()括号将想提取的子字符串括起来。()实际上标记了一个子表达式的开始和结束位置,被标记的每个子表达式会依次对应每一个分组,调用group()方法传人分组的索引即可获取提取的结果
import re content='Hello 1234567 World This is a Regex Demo' result = re.match('^Hello\s(\d+)\sWorld', content) print(result) # <sre.SRE_Match object; span=(0,19),match='Hello 1234567 World'> print(result.group()) # Hello 1234567 World print(result.group(1)) # 1234567 print(result.span()) # (0,19) # 若还有括号可用group(2),group(3)... -
通用匹配 .(点):可以匹配任意字符(除换行符) (星):代表匹配前面的字符无限次 所以 . 在一起可以匹配任意字符
import re content='Hello 1234567 World This is a Regex Demo' result=re.match('^Hello.*Demo$',content) print(result) print(result.group()) print(result.span()) # (0,41) -
贪婪与非贪婪 我们想要中间的数字
import re content='Hello 1234567 World This is a Regex Demo' result=re.match('^Hello.*(\d+).*Demo$',content) print(result) print(result.group(1)) # 7因为.会尽可能多的匹配字符,则\d只要保证至少有一个就好,所以只有7 解决:用.? .*? 是非贪婪,即尽可能少的匹配少的字符,当匹配到Hello后面的空白字符时,再往后的字符就是数字了,而\d+正好可以匹配
import re content='Hello 1234567 World This is a Regex Demo' result=re.match('^Hello.*?(\d+).*Demo$',content) print(result) print(result.group(1)) # 1234567如果匹配的结果在字符串结尾,.*?就有可能匹配不到任何内容了,因为它会匹配尽可能少的字符
import re content='http://weibo.***/***ment/kEra***' result1 = re.match('http.*?***ment/(.*?)',content) result2 = re.match('http.*?***ment/(.*)',content) print('result1',result1.group(1)) # result1 print('result2',result2.group(1)) # result2 kEra*** -
修饰符 一个可选的标志
import re content = '''Hello 1234567 World_This is a Regex Demo ''' result = re.match('^He.*?(\d+).*?Demo$',content) print(result.group(1)) # 加了换行符匹配不到了,直接None了,调用group()方法导致AttributeError # 因为 . 匹配的是除换行符之外的任意字符,当遇到换行,.*?就不匹配了加一个re.S的修饰符可以让.匹配包括换行符在内的所有字符
result = re.match('^He.*?(\d+).*?Demo$',content,re.S) # 1234567 -
转义匹配 若有些目标字符里就含有.,而正则里的.是匹配除换行符之外的所有字符,则可以用转移匹配实现
import re content = '(百度)www.baidu.***' result = re.match('\(百度\)www\.baidu\.***',content) print(result) # sre.SRE Match object;span=(0,17),match='(百度)www.baidu.***'>
-
-
search() 因为match()是从开头开始匹配的,一旦开头不匹配,则整个匹配失败
import re content ='Extra stings Hello 1234567 World_This is a Regex Demo Extra stings' result = re.match('Hello.*?(\d+).*?Demo', content) # 整个正则是字符串中间的一部分,而不是开头开始的一部分 print(result) # 匹配失败,Nonesearch()会在匹配时会扫描整个字符串,然后返回第一个成功匹配的结果,否则None 可以匹配一段HTML文本,假设文本中有换行
result = re.search('<li.*?active.*?singer="(.*?)">(.*?)</a>',html,re.S) if result: # 如果匹配成功 print(result.group(1),result.group(2)) # 齐秦往事随风 -
findall() search()只返回匹配成功的第一个内容 findall()可以返回所有匹配的内容,如果有返回结果的话,返回的是列表类型,所以需要遍历一下一次获取每组内容
results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>',html,re.S) print(results) # 返回的列表中的每个元素都是元组类型 print(type(results)) for result in results: print(result) print(result[0],result[1],result[2]) -
sub() 可以借助正则表达式来修改文本 想要把一串文本中的所有数字都去掉,如果只用字符串的replace()方法,那就太烦琐了,这时可以借助sub()方法
import re content = '54aK54yr5oiR54ix5L2g' content = re.sub('\d+','',content) print(content) # aKyroiRixLg -
***pile() 这个方法可以将正则字符串编译成正则表达式对象,以便在后面的匹配中复用
import re content1 = '2016-12-15 12:00' content2 = '2016-12-17 12:55' content3 = '2016-12-22 13:21' pattern = re.***pile('\d{2}:\d{2}') result1 = re.sub(pattern,'',content1) # 将3个日期中的时间去掉 result2 = re.sub(pattern,'',content2) result3 = re.sub(pattern,'',content3) print(result1,result2,result3)***pile()还可以传入修饰符re.S等,这样在search()那些方法中就不用一直传了
抓取猫眼电影排行
提取出猫眼电影 TOP100的电影名称、时间、评分、图片等信息,提取的结果会以文件形式保存下来 用requests和正则 URL: http://maoyan.***/board/4 点击第二页时,URL变成了http://maoyan.***/board/4?offset=10,多了一个偏移量参数,因为当前页面排名显示的是11-20名 所以,offset代表偏移量,若偏移量为n,则显示电影序号就是n+1到n+10,每页显示10个 所以,如果想获取TOP100电影,只需要分开请求10次,而10次的offset参数分别设置为0,10,20...90即可
-
抓取首页 get_one_page()方法没并且给她出啊如URL地址 然后抓取页面返回页面即可
import requests def get_one_page(url): headers = { 'User-Agent':'Mozilla/5.0(macintosh;Intel Mac OS X 10_13_3)AppleWebKit/537.36(KHTML,like Gecko)Chrome/65.0.3325.162 Safari/537.36' } response = requests.get(url,headers=headers) if response.status_code == 200: return response.text return None def main(): url = 'http://maoyan.***/board/4' html = get_one_page(url) print(html) main()获取到首页的源代码之后,就需要解析页面,提取出我们想要的信息
-
正则提取
import requests import re def get_one_page(url): headers = { 'User-Agent':'Mozilla/5.0(macintosh;Intel Mac OS X 10_13_3)AppleWebKit/537.36(KHTML,like Gecko)Chrome/65.0.3325.162 Safari/537.36' } response = requests.get(url,headers=headers) if response.status_code == 200: return response.text return None def parse_one_page(html): pattern = re.***pile( '<dd>.*?board-index.*?>(.*?)</i>.*?name.*?title="(.*?)".*?star">(.*?)</p>.*?releasetime">(.*?)</p>.*?score.*?>(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>',re.S ) items = re.findall(pattern,html) for item in items: # 可以赋值为一个个的字典,美观 yield { 'index':item[0], 'image':item[1], 'title':item[2].strip(), 'actor':item[3].strip()[3:] if len(item[3]) > 3 else '', 'time':item[4].strip()[5:] if len(item[4]) > 5 else '', 'score':item[5].strip() + item[6].strip() } def main(): url = "http://maoyan.***/board/4" html = get_one_page(url) print(html) parse_one_page(html) main() -
写入文件 将提取到的结果写入文件,这里直接写入到一个文本文件,通过JSON库的dumps()方法实现字典的序列化,并指定ensure_ascii参数为False,这样可以保证输出结果是中文形式而不是Unicode编码
def write_to_file(content): # content参数就是一部电影的提取结果,是一个字典 with open('result.txt','a',encoding='utf-8') as f: print(type(json.dumps(content))) f.write(json.dumps(content,ensure_ascii=False)+'\n')整合
import requests import re import json def get_one_page(url): headers = { 'User-Agent':'Mozilla/5.0(macintosh;Intel Mac OS X 10_13_3)AppleWebKit/537.36(KHTML,like Gecko)Chrome/65.0.3325.162 Safari/537.36' } response = requests.get(url,headers=headers) if response.status_code == 200: return response.text return None def parse_one_page(html): pattern = re.***pile( '<dd>.*?board-index.*?>(.*?)</i>.*?name.*?title="(.*?)".*?star">(.*?)</p>.*?releasetime">(.*?)</p>.*?score.*?>(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>',re.S ) items = re.findall(pattern,html) for item in items: # 可以赋值为一个个的字典,美观 yield { 'index':item[0], 'image':item[1], 'title':item[2].strip(), 'actor':item[3].strip()[3:] if len(item[3]) > 3 else '', 'time':item[4].strip()[5:] if len(item[4]) > 5 else '', 'score':item[5].strip() + item[6].strip() } def write_to_file(content): # content参数就是一部电影的提取结果,是一个字典 with open('result.txt','a',encoding='utf-8') as f: print(type(json.dumps(content))) f.write(json.dumps(content,ensure_ascii=False)+'\n') def main(): url = "http://maoyan.***/board/4" html = get_one_page(url) print(html) for item in parse_one_page(html): write_to_file(item) main() -
分页爬取 遍历一下,给这个链接传人offset 参数,实现其他90部电影的爬取,此时添加如下调用即可:
if __name__ == '__main__': for i in range(10): main(offset=i * 10)整个代码
import json import requests from requests.exceptions import RequestException import re import time def get_one_page(url): try: headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4507.400' } response = requests.get(url,headers=headers) if response.status_code == 200: return response.text return None except RequestException: return None def parse_one_page(html): pattern = re.***pile( '<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name"><a.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>',re.S ) items = re.findall(pattern,html) for item in items: # 可以赋值为一个个的字典,美观 yield { 'index':item[0], 'image':item[1], 'title':item[2], 'actor':item[3].strip()[3:], 'time':item[4].strip()[5:], 'score':item[5] + item[6] } def write_to_file(content): # content参数就是一部电影的提取结果,是一个字典 with open('resultt.txt','a',encoding='utf-8') as f: # 以追加模式 ('a') 打开文件,避免覆盖之前的内容,ensure_ascii=False 确保中文字符正常显示。 f.write(json.dumps(content,ensure_ascii=False)+'\n') def main(offset): url = 'http://maoyan.***/board/4?offset=' + str(offset) html = get_one_page(url) # print(html) for item in parse_one_page(html): print(item) write_to_file(item) if __name__ == '__main__': # 现在猫眼多了反爬虫,如果速度过快,则会无响应,所以这里又增加了一个延时等待 for i in range(10): main(offset=i * 10) time.sleep(1)