小白python爬虫学习3(regular expression正则表达式RE)-淘宝商品,股票实例

依旧这些代码来自北理课程,之后会解释与学习相关知识

一:获取淘宝的商品与价格的代码:

import requests
import re

def getHTMLText(url):
    try:
        r = requests.get(url, timeout=30)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""
    
def parsePage(ilt, html):
    try:
        plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"',html)
        tlt = re.findall(r'\"raw_title\"\:\".*?\"',html)
        for i in range(len(plt)):
            price = eval(plt[i].split(':')[1])
            title = eval(tlt[i].split(':')[1])
            ilt.append([price , title])
    except:
        print("")

def printGoodsList(ilt):
    tplt = "{:4}\t{:8}\t{:16}"
    print(tplt.format("序号", "价格", "商品名称"))
    count = 0
    for g in ilt:
        count = count + 1
        print(tplt.format(count, g[0], g[1]))
        
def main():
    goods = '书包'
    depth = 3
    start_url = 'https://s.taobao.com/search?q=' + goods
    infoList = []
    for i in range(depth):
        try:
            url = start_url + '&s=' + str(44*i)
            html = getHTMLText(url)
            parsePage(infoList, html)
        except:
            continue
    printGoodsList(infoList)
    
main()

第一段:常规操作(在上篇文章中已有介绍)

第二段:会发现有一系列乱七八糟的符号串,这是正则表达式-先学习一下正则

正则表达式是为了简洁的表达一组字符串,也可以说正则表达式是通用的字符串表达框架

举例:“PY”,“PYY”,“PYYY”,“PY....”=“PY+”:“+”表示P后面有无穷多个Y

正则表达在文本处理中比较常用

  1. 如匹配字符串的全部或部分
  2. 同时查找或替换一组字符串
  3. 表达文本类型的特征

如下常用表达式

.

操作符说明实例
.

表示任何单个字符

 
[]字符集,对单个字符给出取值范围

[abc]表示a、b、c,[a-z]表示a到z单个字符

[^]非字符集,对单个字符给出排除范围[^abc]表示非a且非b非c的单个字符
*前一个字符0次或无限次扩展abc*表示ab、abc、abcc
+前一个字符1次或无限次扩展abc+表示abc,abccc
?前一个字符0次或1扩展

abc?表示abc,ab

!左右表达式任意一个abc|def表示abc、def

 

 

 

 

 

 

 

 

操作符说明实例
{m}扩展前一个字符m次

ab{2}c表示abbc

{m,n}扩展前一个字符m至n次ab{1,2}c表示abc、abbc、
^匹配字符串开头^abc表示abc且在一个字符串的开头
$匹配字符串的结尾

abc$表示abc且在一个字符串的结尾

()分组标记,内部只能使用|操作符(abc)表示abc,(abc|def)表示abc、def
\d数字,等价于[0-9] 
\w单词字符,等价于[A-Za-z0-9] 

 

 

 

 

 

 

 

按照课程先尝试一下

正则表达式对应字符串
P(Y|YT|YTH|YTHO)?N

'PN'、'PYN'、PYTN......

PYTHON+'PYTHOM'、'PYTHONN'.....
PY[TH]ON'PYTON','PYTHON'........
PY[^TH]?ON'PYON','PYAON'.......

经典正则表达式实例

对应字符串
^[A-Za-z]+$由26个字母组成的字符串
^[A-Za-z0-9]+$由26个字母和数字组成的字符串

 

^-?\d+$整数形式的字符串
^[0-9]*[1-9][0-9]*$正整数形式的字符串
[1-9]\d{5}中国境内邮政编码,6位
[\u4e00-\u9fa5]匹配中文字符(以utf-8为基础)
\d{3}-\d{8}|\{4}-\d{7}国内电话号码

\d+.\d+.\d+.\d+(不精确)

IP地址字符串形式的正则表达式
\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}(不精确)
  

正则表达式的表示类型有

  1. raw string 类型(原生字符串类型)
  2. string 类型,更繁琐一些

两个类型的区别还是很有意思的,简单介绍下,在string中"\"用作转义符,而在正则表达式中对于有特殊意义的字符前面也要加上"\"才能匹配,如想要匹配"*"就需要"\*",这样区别就很明显了,string类型比raw string类型还要多了一次转义,比如:要匹配"\c"用string类型是这样re.search('\\\\c',text)而用原生字符串表示的话就可以这样re.search("\\c",text),为什么呢???

注意string比raw string多了一次转义,所以'\\\\c'先被string转义为'\\c'然后又在正则中转义为"\c"大致就是这样的一个过程

re库的介绍

import re

导入

re库的主要功能函数

函数说明
re.search(pattern,string,flags=0)在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象
re.match(pattern,string ,flags=0)从一个字符串的开始位置起匹配正则表达式,返回match对象
re.findall(pattern,string,flags=0)搜索字符串,以列表类型返回全部能匹配的字串
re.split(pattern,string,maxsplit=0,flags=0)将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
re.finditer()搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象
re.sub(pattern,repl,string,count=0,flags=0)在一个字符串中替换所有匹配正则表达式的字串,返回替换后的字符串
re.compile(pattern,flags=0)将正则字符串编译成正则表达式对象
pattern正则表达式的字符串string要与正则表达式匹配的字符串flag(正则表达式的控制标记)re.I 忽略正则表达式的大小写 
re.M ‘^’操作符能将给定字符串的每行当做匹配开始
re.S '.'能够匹配处换行外的所有字符

表格中提到返回match对象,那么这个match对象是什么呢???

Match的属性

属性说明
.string待匹配的文本
.re匹配时使用的pattern对象(正则表达式)
.pos正则表达式搜索文本的开始位置
.endpos正则表达式搜索文本的结束位置

 

Match的方法

方法说明
.group(0)获得匹配后的字符串
.start()匹配字符串在原始字符串的开始位置
.end()匹配字符串在原始字符串的结束位置
.span()返回(.star(),.end())

re库的俩种等价用法

  1. 一次性操作:rst=re.search(r'[1-9]\d{5}','BIT 100081')
  2. 编译后多次操作:pat=re.compile(r'[1-9]\d{5}')         rst=pat.search('BIT 100081')

问题又来了,假设现在要匹配的字符串是这样的   re.search(r'PY.*N','PYANBNCNDN')

这个时候函数该返回什么呢???事实上re库采用的是贪婪匹配就是匹配的尽量多输出最长的字串,所以返回的是PYANBNCNDN

那么有时候也会想要最短的字符串此时如何改进???

操作符说明
*?前一个字符0次或无限次扩展,最小匹配
+?前一个字符1次或无限次扩展,最小匹配
??前一个字符0次或一次扩展,最小匹配
{m,n}?

扩展前一个字符m至n次,最小匹配

 

掌握了上面的知识,来看一下这第二段函数究竟想表达什么

def parsePage(ilt, html):
    try:
        plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"',html)
        tlt = re.findall(r'\"raw_title\"\:\".*?\"',html)
        for i in range(len(plt)):
            price = eval(plt[i].split(':')[1])
            title = eval(tlt[i].split(':')[1])
            ilt.append([price , title])
    except:
        print("")


        plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"',html)这句话,传了一个原生字符串 r'\"view_price\"\:\"[\d\.]*\"'这个要匹配什么呢?其实通过淘宝商品网页的分析发现HTML页面的价格标签为"view_price":"158.00"

这个表达式就是为了可以匹配到这个内容
        tlt = re.findall(r'\"raw_title\"\:\".*?\"',html):同理这个是为了可以匹配到商品的名字


        for i in range(len(plt)):
            price = eval(plt[i].split(':')[1])
            title = eval(tlt[i].split(':')[1])
            ilt.append([price , title])
    except:
        print("")

这里的len用于返回对象的长度,我感觉应该是字串的个数。eval函数在这里的作用主要还是用于去掉双引号,eval函数在这里主要是用来去掉双引号,eval函数的其他用途是什么呢?请看这篇

eval函数的作用

剩下的函数便不用在解释了,所以这个函数便是从返回的HTML页面找到名称和价格并返回列表

最后一个函数:就不介绍了,有些简单。。。

 

 

 

二:获取股票相关信息的代码,之后会一一解释作用

#CrawBaiduStocksB.py
import requests
from bs4 import BeautifulSoup
import traceback
import re

def getHTMLText(url, code="utf-8"):
    try:
        r = requests.get(url)
        r.raise_for_status()
        r.encoding = code
        return r.text
    except:
        return ""

def getStockList(lst, stockURL):
    html = getHTMLText(stockURL, "GB2312")
    soup = BeautifulSoup(html, 'html.parser') 
    a = soup.find_all('a')
    for i in a:
        try:
            href = i.attrs['href']
            lst.append(re.findall(r"[s][hz]\d{6}", href)[0])
        except:
            continue

def getStockInfo(lst, stockURL, fpath):
    count = 0
    for stock in lst:
        url = stockURL + stock + ".html"
        html = getHTMLText(url)
        try:
            if html=="":
                continue
            infoDict = {}
            soup = BeautifulSoup(html, 'html.parser')
            stockInfo = soup.find('div',attrs={'class':'stock-bets'})

            name = stockInfo.find_all(attrs={'class':'bets-name'})[0]
            infoDict.update({'股票名称': name.text.split()[0]})
            
            keyList = stockInfo.find_all('dt')
            valueList = stockInfo.find_all('dd')
            for i in range(len(keyList)):
                key = keyList[i].text
                val = valueList[i].text
                infoDict[key] = val
            
            with open(fpath, 'a', encoding='utf-8') as f:
                f.write( str(infoDict) + '\n' )
                count = count + 1
                print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="")
        except:
            count = count + 1
            print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="")
            continue

def main():
    stock_list_url = 'https://quote.eastmoney.com/stocklist.html'
    stock_info_url = 'https://gupiao.baidu.com/stock/'
    output_file = 'D:/BaiduStockInfo.txt'
    slist=[]
    getStockList(slist, stock_list_url)
    getStockInfo(slist, stock_info_url, output_file)

main()

由于百度股票已经凉了,所以。。。。。。。。。。这个代码实践意义有些过小,不过没关系,它还有学习意义呀。。。。

这里最重要的还是第二个函数 def getStockList(lst, stockURL):

def getStockInfo(lst, stockURL, fpath):
    count = 0
    for stock in lst:
        url = stockURL + stock + ".html"
        html = getHTMLText(url)
        try:
            if html=="":
                continue
            infoDict = {}
            soup = BeautifulSoup(html, 'html.parser')
            stockInfo = soup.find('div',attrs={'class':'stock-bets'})

            name = stockInfo.find_all(attrs={'class':'bets-name'})[0]
            infoDict.update({'股票名称': name.text.split()[0]})
            
            keyList = stockInfo.find_all('dt')
            valueList = stockInfo.find_all('dd')
            for i in range(len(keyList)):
                key = keyList[i].text
                val = valueList[i].text
                infoDict[key] = val
            
            with open(fpath, 'a', encoding='utf-8') as f:
                f.write( str(infoDict) + '\n' )
                count = count + 1
                print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="")
        except:
            count = count + 1
            print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="")
            continue

 关于BeautifulSoup类上一篇已讲过小白python爬虫学习2(初识Request,Beautifulsoup,print)在此不再赘述

关于split()[0]-----split函数默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等取分割的第一个

关于不换行的进度条 关键"\r"将光标提到第一行首位。。。。。。

OK终于写完了,以后再补充

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值