正则、xpath、bs4的使用和语法

单字符匹配

. 除换行符之外的任意字符
\d 表示数字
\D 匹配非数字
\w 匹配单词字符[a-z,A-Z,0-9]
\W 匹配非单词字符
\s 匹配空白字符,空格,\n \t…
\S 匹配非空白字符
^ 匹配以…开头
$ 匹配以…结尾
[0-9] => \d 匹配0-9

多字符匹配(贪婪匹配)

  • 匹配*前面的字符任意次数
  • 匹配+前面的字符至少一次
    ?匹配?前面的字符0-1次
    {n,m}匹配{n,m}前面的字符n-m次
    多字符匹配(非贪婪匹配)
    *?
    +?
    ??
    其他
    ()分组
    |逻辑或
    \转义字符
    re模块下的方法
    re.compile():构建正则表达式对象
    re.match():从起始位开始匹配,单次匹配,如果匹配到结果立即返回,反之,返回None
    re.search():在整个字符串中进行匹配,单次匹配,如果匹配到结果立即返回,反之,返回None
    re.findall():匹配出整个字符串中,所有符合正则规则的结果,返回一个列表
    re.finditer():匹配出整个字符串中,所有符合正则规则的结果,返回的是一个可迭代对象
    re.sub():根据正则表达式进行字符串替换
    re.split():根据正则表达式进行分割

正则的用法

def get_rank_data(url='http://top.hengyan.com/dianji/default.aspx?p=1'):
    #构建请求头
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
    }
    # url, \目标url
    # data=None, \默认为None表示是get请求,如果不为None说明是get请求
    # timeout 设置请求的超时时间
    # cafile=None, capath=None, cadefault=False,:证书相关参数
    # context=None :忽略证书认证
    #urlopen不能添加请求头
    # response = request.urlopen(url=url,timeout=10)

    #添加请求头
    req = request.Request(url=url,headers=headers)
    response = request.urlopen(req,timeout=10)

    #响应状态码
    code = response.status
    #当前请求的url地址
    url = response.url
    print(code,url)

    b_content = response.read()
    # bytes -> str: decode
    # str -> bytes: encode
    # print(b_content)
    html = b_content.decode('utf-8')
    # print(html)
    # #文件操作
    # """
    # w:    w+:    wb:    wb+    a:    a+:    ab:    ab+:    r:    rb:
    # """
    # with open('hengyan.html','w') as file:
    #     file.write(html)

    #证据正则表达式解析数据
    # re.S 修饰:表示.可以匹配换行符

    pattern = re.compile('<div\sclass="list">(.*?)</div>',re.S)
    ul_str = re.findall(pattern,html)[0]

    pattern1 = re.compile('<ul.*?>(.*?)</ul>',re.S)
    li_strs = re.findall(pattern1,ul_str)[1:]

    for li_str in li_strs:
        # print(li_str)
        pattern = re.compile(
            '<li\sclass="num">(.*?)</li>'+
            '.*?<a.*?>(.*?)</a>'+
            '.*?<li.*?>(.*?)</li>'+
            '.*?<li.*?>(.*?)</li>'+
            '.*?<li.*?>(.*?)</li>'+
            '.*?<li.*?>(.*?)</li>',
            re.S
        )

        data = re.findall(pattern=pattern,string=li_str)[0]
        print(data)

    #提取下一页:
    if '下一页' in html:
        #说明还存在下一页
        pattern = re.compile('<span\sclass="pageBarCurrentStyle">(.*?)</span>',re.S)
        current_page = int(re.findall(pattern,html)[0])
        next_page = current_page+1
        #构造下一页的URL地址
        next_page_url = re.sub('\d+',str(next_page),url)
        print(next_page_url)
        get_rank_data(next_page_url)
    else:
        print('数据提取完毕')

if __name__ == '__main__':
    get_rank_data()

xpath

安装:pip install lxml
引用:from lxml import etree
创建etree对象进行指定数据解析
1.本地
etree = etree.parse(‘本地路径’)
etree.xpath(‘xpath表达式’)
2.网络
etree = etree.HTML(‘网络请求到页面的数据’)
etree.xpath(‘xpath表达式’)
常用的xpath表达式:
1.属性定位:
找到class属性值为song的div标签
//div[@class=‘song’]
2.层级索引定位
找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a
//div[@class=‘tang’]/ul/li[2]/a
3.逻辑运算
找到href属性值为空且class属性值为du的a标签
//a[@href=’’ and @class=‘du’]
4.模糊匹配
/表示获取某个标签下的文本内容 //div[@class=‘song’]/p[1]/text()
//表示获取某个标签下的文本内容和所有子标签下的文本内容 //div[@class=‘tang’]//text()
5.取属性
//div[@class=‘tang’]//li[2]/a/@href

class HengYanSpider(object):

    def __init__(self):
        self.first_url = 'http://all.hengyan.com/1/0_0_0_0_0_0_0_0_0_1.aspx'
        self.default_headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
        }

    def get_noval_url(self, url=None):
        url = self.first_url if not url else url
        """获取小说详情的url地址"""
        html = self.send_request(url)
        if html:
            # 解析数据(获取xpath解析器)
            etree_html = etree.HTML(html)
            noval_urls = etree_html.xpath('//li[@class="bookname"]/a[1]/@href')
            for noval_url in noval_urls:
                self.get_noval_detail(noval_url)

            # 获取下一页
            if '下一页' in html:
                # 说明还存在下一页
                current_page = int(self.extract_first(etree_html.xpath('//span[@class="pageBarCurrentStyle"]/text()]')))
                next_page = current_page + 1
                next_page_url = re.sub('\d+.aspx', str(next_page)+'.aspx', url)
                self.get_noval_url(next_page_url)
            else:
                print('数据提取完毕')
        else:
            print('数据获取失败')

    def get_noval_detail(self, noval_url):
        """获取书籍详情的页面内容,解析数据"""
        html = self.send_request(noval_url)
        if html:
            # 解析数据(获取xpath解析器)
            etree_html = etree.HTML(html)
            # print('得到了详情页面')
            noval_dict = {}
            # 书号
            book_id = self.extract_first(etree_html.xpath('//div[@class="dh"]/p/label/text()'))
            noval_dict['book_id'] = re.search('\d+', book_id).group()
            # 热度
            noval_dict['hot'] = self.extract_first(etree_html.xpath('//p[@class="wendu"]/b/text()'))
            # 火车票
            noval_dict['hot_track'] = self.extract_first(
                etree_html.xpath('//div[@class="piao"]/p[2]/span[@class="huocolor"]/text()'))
            # 冰票
            noval_dict['bing_track'] = self.extract_first(
                etree_html.xpath('//div[@class="piao"]/p[2]/span[@class="bingcolor"]/text()'))
            # 金笔
            noval_dict['jingbi'] = self.extract_first(etree_html.xpath('//div[@class="jinbi"]//li[1]/p[2]/text()'))
            # 标题
            noval_dict['title'] = self.extract_first(etree_html.xpath('//h2/text()'))
            # 简介
            noval_dict['content'] = self.extract_first(
                etree_html.xpath('//p[@class="intro ih1"]/text()|//p[@class="intro ih2"]/text()'))
            # 作者
            noval_dict['author'] = self.extract_first(etree_html.xpath('//div[@id="ainfo"]/p/span/a[2]/text()'))

            print(noval_dict)
            self.save_data(noval_dict)

    def save_data(self, noval_dict):
        """保存数据"""
        pass

    def extract_first(self, data, default=''):
        if len(data) > 0:
            return data[0]
        return default

    def send_request(self, url, header=None, data=None, method="GET"):
        """发送请求"""
        header = self.default_headers if not header else header

        if method == 'GET':
            # 发送get请求
            response = requests.get(url=url, params=data, headers=header)
        else:
            # 发送post请求
            response = requests.post(url=url, data=data, headers=header)

        if response.status_code == 200:
            # 请求成功,返回页面源码
            return response.text


if __name__ == '__main__':
    spider = HengYanSpider()
    spider.get_noval_url()

bs4(python独有简单便捷和高效)

环境安装:pip install lxml bs4用到lxml库,如果没有安装过lxml库的时候,需要安装一下
代码使用流程:
核心思想:可以将html文档可以转换成BeautifulSoup对象,调用该对象中的属性和方法进行
1.导包
from bs4 import BeautifulSoup
2.创建BeautifulSoup对象
a.本地
Beautiful(‘open(‘本地的html文件’)’,‘lxml’)
b.网络
Beautiful(‘网络请求到的页面数据’,‘lxml’)
属性和方法:
1.根据标签名查找
soup.a 只能找到第一个符合要求的标签
2.获取属性
soup.a.attrs 获取a所有的属性和属性值,返回一个字典
soup.a.attrs[‘href’] 获取href属性
soup.a[‘href’] 也可简写为这种形式
3.获取内容
soup.a.string /text()
soup.a.text //text()
soup.a.get_text() //text()
如果标签还是标签,那么string获取到的结果为none,而其他两个,可以获取文本内容
4.find:找到第一个符合要求的标签
soup.find(‘a’) 找到第一个符合要求的
soup.find(‘a’,title=‘xxx’)
soup.find(‘a’,alt=‘xxx’)
soup.find(‘a’,class=‘xxx’)
soup.find(‘a’,id=‘xxx’)
5.find_All:找到所有符合要求的标签
soup.find_All(‘a’)
soup.find_All([‘a’,‘b’]) 找到所有的a和b标签
soup.find_All(‘a’,limit=2) 限制前两个
6.根据选择器选择指定的内容
select:soup.select(’#feng’)
常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
层级选择器:
div .dudu #lala .name .xixi 下面好多级 div//img
div > p > a > .lala 只能是下面一级 div/img
select选择器返回永远是列表,需要通过下标提取指定对象
class HengYanSpider(object):

    def __init__(self):
        self.first_url = 'http://all.hengyan.com/1/0_0_0_0_0_0_0_0_0_1.aspx'
        self.default_headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
        }

    def get_noval_url(self, url=None):
        url = self.first_url if not url else url
        """获取小说详情的url地址"""
        html = self.send_request(url)
        if html:
            bs_soup = BeautifulSoup(html, 'lxml')
            lis = bs_soup.find_all(name='li', attrs={'class': 'bookname'})
            for li in lis:
                # a_list = li.find_all(name='a')
                # if len(a_list) > 0:
                #     url = a_list[0].attrs['href']
                a_list = li.select('a')
                if len(a_list) > 0:
                    url = a_list[0].attrs['href']
                    self.get_noval_detail(url)

    def get_noval_detail(self, noval_url):
        """获取书籍详情的页面内容,解析数据"""
        html = self.send_request(noval_url)
        if html:
            # 解析数据(获取xpath解析器)
            bs_soup = BeautifulSoup(html, 'lxml')
            # print('得到了详情页面')
            noval_dict = {}
            # 书号
            book_id = bs_soup.select('div.dh p label')[0].get_text()
            noval_dict['book_id'] = re.search('\d+', book_id).group()
            # 热度
            noval_dict['hot'] = bs_soup.select('p.wendu b')[0].get_text()
            # 火车票
            noval_dict['hot_track'] = bs_soup.select('div.piao p')[1].select('.huocolor')[0].get_text()
            # 冰票
            noval_dict['bing_track'] = bs_soup.select('div.piao p')[1].select('.bingcolor')[0].get_text()
            

            print(noval_dict)
            # self.save_data(noval_dict)

    def save_data(self, noval_dict):
        """保存数据"""
        pass

    def extract_first(self, data, default=''):
        if len(data) > 0:
            return data[0]
        return default

    def send_request(self, url, header=None, data=None, method="GET"):
        """发送请求"""
        header = self.default_headers if not header else header

        if method == 'GET':
            # 发送get请求
            response = requests.get(url=url, params=data, headers=header)
        else:
            # 发送post请求
            response = requests.post(url=url, data=data, headers=header)

        if response.status_code == 200:
            # 请求成功,返回页面源码
            return response.text

if __name__ == '__main__':
    spider = HengYanSpider()
    spider.get_noval_url()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容