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

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

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终于写完了,以后再补充