Python_day17--正则表达--分组,贪婪匹配,切分字符串,爬取贴吧的邮箱

一、分组

除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用 () 表示的就是要提取的分组(Group)

例如:

^(\d{3})-(\d{3,8})$ 分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:

m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
print(m)
print(m.group(0))
print(m.group(1))
print(m.group(2))

如果正则表达式中定义了组,就可以在 Match 对象上用 group() 方法提取出子串来。

注意到 group(0) 永远是原始字符串, group(1) 、 group(2) ......表示第 1、2、......个子串。提取子串非常有用。

二、贪婪匹配

需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。

举例如下,匹配出数字后面的 0 :

import re

s = re.match(r'^(\d+)(0*)$', '102300')
print(s.groups())

由于 \d+ 采用贪婪匹配,直接把后面的 0 全部匹配了,结果 0* 只能匹配空字符串了。

必须让 \d+ 采用非贪婪匹配(也就是尽可能少匹配),才能把后面的 0 匹配出来,加个 ? 就可以让 \d+ 采用非贪婪匹配;

例如:

import re

s = re.match(r'^(\d+?)(0*)$', '102300')
print(s.groups())

三、切分字符串

用正则表达式切分字符串比用固定的字符更灵活,请看正常的切分代码:

print('a b   c'.split(' '))

无法识别连续的空格,用正则表达式试试:

import re

print(re.split(r'\s+','a b   c'))

无论多少个空格都可以正常分割。加入 , 试试:

import re

print(re.split(r'[\s\,]+','a   ,  ,  b  ,  c'))

再加入 ; 试试:

import re

print(re.split(r'[\s\,\;]+','a; ,b;,c'))

如果用户输入了一组标签,下次记得用正则表达式来把不规范的输入转化成正确的数组。


四、应用案例--在贴吧上爬取邮箱

1、使用协程
from concurrent.futures import ThreadPoolExecutor  
from urllib import request  
import re  
from gevent import monkey  
import threading  
# 打补丁, 自动修改协程中需要的一些标准库;  
monkey.patch_socket()  
import gevent  
  
from mytime import timeit  
  
url = 'http://tieba.baidu.com/p/2314539885'  
EmailLi = []  
  
def get_content(url):  
    """获取网页源代码"""  
    # 1. 下载网页源代码到本地, 获取帖子总页数;  
    # urlObj = request.urlopen(url, timeout=60)  
    # content = urlObj.read().decode('utf-8')  
    with request.urlopen(url, timeout=60) as urlObj:  
        content = urlObj.read().decode('utf-8')  
        return content  
  
  
def get_page(url):  
    content = get_content(url)  
    # <a href="/p/2314539885?pn=31">尾页</a>  
    pattern = r'<a href="/p/.*pn=(\d+)">尾页</a>'  
    page = re.findall(pattern, content)[0]  
    return int(page)  
  
  
def get_all_url(url, page):  
    """生成所有页的帖子url地址"""  
    url_li = []  
    # page: 31  0,1,2,3,4.....30  
  
    for i in range(page):  
        new_url = url + '?pn=%d' %(i+1)  
        url_li.append(new_url)  
    return url_li  
  
  
def get_email(url):  
    content = get_content(url)  
    pattern = r'[a-zA-Z0-9_]+@\w+\.com'  
    EmailLi.extend(re.findall(pattern, content))  
    print(re.findall(pattern, content))  
  
# 结论:  
#   1.  如果代码是IO密集型, 建议选择多线程;  
#   2.  如果是计算密集型, 建议选用多进程;  
 
@timeit  
def geventMain():  
    # url = 'http://tieba.baidu.com/p/2314539885'  
    page = get_page(url)  
    url_li = get_all_url(url, page)  
    gevents = [gevent.spawn(get_email, url) for url in url_li]  
    gevent.joinall(gevents)  
geventMain()

2、一般方式

from urllib import request

import re

url = 'http://tieba.baidu.com/p/2314539885'



#爬取每个页面上的所有内容
def get_content(url):
    with request.urlopen(url) as f:
        content = f.read().decode('utf-8')
        return content

#抓取需要的页面信息,这里要的是页面的总页码
def get_page(url):
        content = get_content(url)
        # < a href = "/p/2314539885?pn=31" >尾页 < / a >
        pattern = r'<a href="/p/.*?pn=(.*)">尾页</a>'
        return int(re.findall(pattern, content)[0])

#生成新的每一页的网络地址,等同于重新构造了网址
def create_url(url, page):
    url_li = []
    for i in range(page):
        # print("第%s页" %(i+1))
        new_url = url+'?pn=%d' %(i+1)
        url_li.append(new_url)
    return url_li


#利用正则匹配每一页中合法的邮箱地址
def get_email(url):
    content = get_content(url)
    pattern = r"[a-zA-Z0-9_]+@\w+\.com"
    return re.findall(pattern, content)



page = get_page(url)
print("贴吧总共有%d页, 正在爬取中......."%(page))


url_li = create_url(url,page)

for url in url_li:
    print(get_email(url))

五、爬取银行信息

要求:我们需要http://www.cbrc.gov.cn/chinese/jrjg/index.html 网站的所有银行的信息 ,并且保存在文件中;

注意:下面的爬取也许会被封掉你的IP,所以你需要检查好你的代码进行尽量少的爬取动作,其次可以把它的被荣爬取并且现保存在你的文件中,方便我们后续的操作;

下面是我们爬取网站内容的代码:

def get_content(url):
    # 1. ???????;
    headers = {'User-agent': 'FireFox/12.0'}
    req = request.Request(url, headers=headers)

    with urlopen(req) as urlObj:
        content =  urlObj.read().decode('utf-8')

    with open('bank.txt', 'w') as f:
        f.write(content)
        print('write success')
get_content(url)

下面的是我们通过正则过滤网页中我们需要的信息:

import re

url = 'http://www.cbrc.gov.cn/chinese/jrjg/index.html'



def get_file_content(filName):
    with open(filName) as f:
        content = f.read().replace('\t', '')
        return content

# print(get_file_content('bank.txt'))


def get_bank_info(content):
    #<a href="http://www.homecreditcfc.cn/zh/DefaultTianJin.aspx" target="_blank"  style="color:#08619D">xxxx</a>

    # pattern = r'<a href="(http://www(?:\.\w*){1,3})>\s*(.*)\s*</a>"'
    pattern = r'<a href="(http://www.*)" target="_blank" style="color:#08619D">\s*(.*)\s*'
    c = re.findall(pattern,content)
    return c
# a = get_bank_info(get_file_content('bank.txt'))
# print(len(a))

# for i in a:
#     print(i)


def write_to_file(filename, bank_li):
    with open(filename, 'w') as f:
        f.write("银行名称\tURL\n")
        for url,name in bank_li:
            f.write("%s\t%s\n" %(name.strip(), url))

    print("写入成功!")



bank_li = get_bank_info(get_file_content('bank.txt'))
for ur, name in bank_li:
    print(ur, '\t', name)
write_to_file('bank1.txt', bank_li)





猜你喜欢

转载自blog.csdn.net/biu_biu_0329/article/details/80765331