爬虫基本工系列之--数据获取

Posted 一腔诗意醉了酒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了爬虫基本工系列之--数据获取相关的知识,希望对你有一定的参考价值。


1. 字符串操作

1.1 字符串截取

str = "helloWorld"

# 1. 字符串截取
# 格式 字符串 [开始位置: 结束位置 :间隔位置 ]
# 间隔位置 正数从左边开始,负数从右边开始

print( str[0:3] ) # hel
print( str[1:3] ) # el
print( str[1:7:2] ) # elw
print( str[::-1] ) # dlroWolleh
print( str[-1:-3: -2]) # d
print( str[-3:]) # rld

print( str[:-5:-3]) # do
# --------------------------

1.2 字符串查找

 # 2.查找
# 格式 字符串.find( '要查找的内容[,开始位置,结束位置]' )
# 开始位置和结束位置表示要查找的范围,若为空,则表示查找所有。
# 找到目标后,返回目标第一位内容所在的下标,没找到返回 -1 


str = "helloWorld"
a = str.find('l') 
print(a)                # 2

print(str.find('o',5))  # 6

print(str.find('e',2,3)) # -1


1.3 字符串替换


# 3. 替换
# 格式 字符串.replace( '被替换内容','替换后的内容' )
# 需要注意的是,使用replace 替换后字符串仅为临时变化,需要使用变量接收才可以保存
# 值得注意的是,所有匹配的项都会被替换
str = "hello world"
a = str.replace('h','g')
print(a,str)                # gelloWorld helloWorld
b = str.replace('o','gg')
print(b, str)               # hellggWggrld helloWorld 
print(str.replace('o',''))      # hellWrld
 


 # ---------

1.4 字符串分割


# 4. 分割
# 格式 str.split( '分割符', 分割次数 )
# 如果分割次数为空,则全部分割
# 返回列表
# 注意分割符不能空(可以是空格)

str = "hello world"
a = str.split('e')
print(a)                # ['h', 'llo world']
print( str.split(' '))  # ['hello', 'world']


2. 正则表达式

正则表达式是用于处理字符串的强大工具,拥有自己独特的语法一独立的处理引擎,效率上可能不如字符串处理方法,但是功能十分强大。且大部分语法都是通用的,与编程语言无关,不被支持的也是不常用的部分。


2.1 正则语法

2.1.1 常用元字符

  1. ^ 匹配开始位置
  2. $ 匹配结尾位置
  3. . 匹配换行符(\\r\\n)之外的任意字符,简称通配符
  4. ? 匹配前面子表达式零次或—>懒惰模式
  5. * 匹配前面子表达式零次或多次 —>贪婪模式
  6. + 匹配前面子表达式一次或多次 —>占有模式
  7. \\ 转义符
  8. { n, m } 匹配闭区间[ n -> m ]
  9. [ ] 一个字符的集合,比如[1-9]将匹配1到9中的任意一个字符
  10. ( ?#注释 ) 添加注释,括号内的内容不作为匹配内容

2.1.2 常用的转义字符(小写)

  1. \\b 匹配一个单词边界(开头或结尾),也就是指单词和空格间的位置(开始和结束)。例如, ‘er\\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。
  2. \\d 匹配任意十进制数字(0-9)
  3. \\s 任何空白符
  4. \\w 字母,数字,下划线

2.1.3 常用的几种反义字符(大写)

  1. \\B 匹配不是单词开头或结束的位置
  2. \\D 非数字
  3. \\S 非( 任何空白符 )
  4. \\W 非(字母,数字,下划线 )

2.1.4 懒惰限定符(?的扩展)

  1. *?” 重复任意次,但尽可能少重复

    如 “acbacb” 正则 “a.*?b” 只会取到第一个"acb" 原本可以全部取到但加了限定符后,只会匹配尽可能少的字符 ,而"acbacb"最少字符的结果就是"acb"

  2. +?” 重复1次或更多次,但尽可能少重复

    与上面一样,只是至少要重复1次

  3. ??” 重复0次或1次,但尽可能少重复

    如 “aaacb” 正则 “a.??b” 只会取到最后的三个字符"acb"

  4. { n,m }?” 重复n到m次,但尽可能少重复

    如 “aaaaaaaa” 正则 “a{0,m}” 因为最少是0次所以取到结果为空

  5. { n, }?” 重复n次以上,但尽可能少重复

    如 “aaaaaaa” 正则 “a{1,}” 最少是1次所以取到结果为 “a”

2.1.5 分组与捕获

分组与捕获详情看博客园

  • 分组
    分组是用圆括号“()”括起来的正则表达式,匹配出的内容就表示一个分组。分组有一个例外的情况,分组也可以不使用圆括号,而是使用 | 元字符来表示分组,| 的两侧是两个分组,例如,exp1 | exp2表示两个分组,在严格意义闪给,不认为由|构成的正则表达式是分组。

    分组和捕获在正则表达式中有着密切的联系,一般情况下,分组即捕获,都用小括号完成:

(exp):分组,并捕获该分组匹配到的文 本
( ? : exp ):分组,但不捕获该分组匹配到的文本

  • 捕获
    什么是捕获呢?使用小括号指定一个子表达式后,子表达式匹配的文本(即匹配的内容)可以在其他子表达式中重复使用。

2.1.5.1 定义分组

定义分组的三种形式:

( exp ) :把括号内的正则作为一个分组,系统自动分配组号,可以通过分组号引用该分组;
(? P <name> exp):定义一个命名分组,分组的正则是exp,系统为该分组分配分组号,可以通过分组名或分组号引用该分组;
( ?: exp):定义一个不捕获分组,该分组只在当前位置匹配文本,在该分组之后,无法引用该分组,因为该分组没有分组名,没有分组号,也不会占用分组编号;


1,分组编号

在正则表达式中,分组编号是自动进行的。当使用圆括号表示分组时,从正则表达式的左边开始看,看到的第一个左括号 “(” 表示第一个分组,第二个 “(” 表示第二个分组,依次类推,需要注意的是,有一个隐含的全局分组(分组编号是0),就是整个正则表达式。默认情况下,正则表达式为每个分组自动分配一个组号,规则是:组号从1开始,从左向右,组号依次加1(base+1),例如,第一个分组的组号为1,第二个分组的组号为2,以此类推。

2,分组命名

分组不仅有编号,还能为分组设置名称,在Python中,使用(?Pexp)为正则表达式exp设置别名。

3,无捕获分组

无捕获分组没有名称,也没有编号,因此,无法引用无捕获分组,无捕获分组不会占用分组编号。


2.2 正则处理函数

2.2.1 re.match 函数

re.match( pattern, string, flags = 0)

尝试从字符串的开头开始匹配一个模式,如果成功,就返回一个匹配成功的对象,否则返回none

‘’’
pattern : 匹配的正则表达式
string : 要匹配的字符串
flags :标志位,用于控制正则表达式,如是否区分大小写,是否多行匹配等
flags的可选值:
re.l : 忽略大小写
re.M : 多行模式,改变^ 和 $ 的行为



import re

text = "This is the last one "
res = re.match( '(.*) is (.*?) .*', text, re.M | re.I )
if res:
    print(res)                              # <re.Match object; span=(0, 21), match='This is the last one '>
    print(len(text))                        # 21 
    print('res.group()-->',res.group())
    print('res.group(0)-->',res.group(0))
    print('res.group(1)-->',res.group(1))
    print('res.group(2)-->',res.group(2))
    print('res.groups()-->',res.groups())
    """ 结果
    res.group()--> This is the last one
    res.group(0)--> This is the last one
    res.group(1)--> This
    res.group(2)--> the
    res.groups()--> ('This', 'the')
    """
else:
    print("NO MATCH!!")

测试( .*? )

# 2(.*) 学习    ``
import re
# 2(.*) 学习    
import re
text = "This is the last one "
res1 = re.match( '(.*) is (.*) (.*)', text, re.M | re.I )       # ('This', 'the last one', '')	1
res2 = re.match( '(.*) is (.*) (.*?)', text, re.M | re.I )      # ('This', 'the last one', '')	2
res3 = re.match( '(.*) is (.*?) (.*)', text, re.M | re.I )      # ('This', 'the', 'last one ') 	3
res4 = re.match( '(.*?) is (.*) (.*)', text, re.M | re.I )      # ('This', 'the last one', '') 	4
res5 = re.match( '(.*?) is (.*?) (.*?)', text, re.M | re.I )    # ('This', 'the', '') 			5
res6 = re.match( '(.*?)(.*?)(.*?)', text, re.M | re.I )         # ('', '', '')					6

print(res1.groups())
  • 匹配结果解释
    因为re.match 是尝试从字符串开头开始匹配所写的模式串,匹配成功则返回
    所以匹配的步骤是:
    在这里插入图片描述
  1. 匹配主串与模式串的长度,如果主串的长度比模式串的长度(指的是正则生效是所需要的最小子串长度)要小,则直接返回none
  2. 长度没问题才开始匹配

结果解释

1.res1的结果注意is前后的空格匹配,匹配完了This is得到第一个分组This ,因为第二个分组优先贪婪于第三个分组,所以,第二个分组的结果是the last one,第三个分组可以匹配任意字符串的字符,所以可以是空串注意要整体匹配,就是说在匹配前面的字符串也要兼顾后面能不能匹配
2. res2res1差不多
3. res3 要注意第二个分组的懒惰模式,所以第二个分组匹配到the
4. res4,res5 差不多
5. res6要注意都是懒惰模式,大家都不想匹配所以得到了三个空的分组。


2.2.2 re.search 函数

res.search( pattern, string , flags = 0)

用法跟re.match()类似,但两者的匹配逻辑不一样

  1. re.match是从字符串开头开始匹配,如果开始不符合,就匹配失败
  2. re.search 是匹配整个字符串,直到找到第一个匹配的模式串,否则才返回none.
coding
import re

text = 'abbacb'

res1 = re.match('(b)(.*?)(.*)', text, re.M | re.I)
try:
    print(res1.groups())

except:
    print('匹配失败',res1)


res2 = re.search('(b)(.*?)(.*)', text, re.M | re.I)
try:
    print(res2.groups())
except:
    print('匹配失败',res2)


'''结果
匹配失败 None
('b', '', 'bacb')

'''

  • 结果分析
  1. 可以看到re,match因为开头的a不匹配,所以返回了none,注意注意如果不做异常处理的话,会出现问题,因为none没有属性方法
  2. re.search() 匹配成功。

2.2.3 re.findall

  • 用法跟re.search一致,只是search匹配的是一个,成功之后返回一个对象,但是findall是匹配所有成功的子串,并返回一个列表

在这里插入图片描述
在这里插入图片描述

2.2.4 re.sub函数

re.sub ( pattern, repl, string, count=0, flags = 0 )

  • re.sub 函数用于替换字符串中的匹配项,如果没有匹配的项,则返回没有匹配的字符串
# pattern 	->匹配的正则表达式
# repl 		->用于替换的字符串
# string	->要被替换的字符串
# count 	->替换的次数
# flags		-> 标志位,用于控制正则表达式的匹配方式,跟match的一致

coding

import re

# 将后四位的数字替换成0
replace_value = re.sub('\\d{4}$', '0000', '123451234')
print(replace_value)    # 123450000

2.2.5 re.split函数

re.split( pattern, string, [,maxsplit])

用来匹配 pattern 的子串来分割字符串,返回一个列表,注意分隔符不进入结果列表

import re

res = re.split('a', 'abacbdbcaacd')
print(res)

res = re.split('a', 'bbabacbdbcaacd')
print(res)

res = re.split('a', 'bbabacbdbcaacd', 2)
print(res)

'''

['', 'b', 'cbdbc', '', 'cd']
['bb', 'b', 'cbdbc', '', 'cd']
['bb', 'b', 'cbdbcaacd']

'''


2.2.99 附re.pyi 的部分源码


if sys.version_info >= (3, 6):
    import enum
    class RegexFlag(enum.IntFlag):
        A: int
        ASCII: int
        DEBUG: int
        I: int
        IGNORECASE: int
        L: int
        LOCALE: int
        M: int
        MULTILINE: int
        S: int
        DOTALL: int
        X: int
        VERBOSE: int
        U: int
        UNICODE: int
        T: int
        TEMPLATE: int

    A = RegexFlag.A
    ASCII = RegexFlag.ASCII
    DEBUG = RegexFlag.DEBUG
    I = RegexFlag.I
    IGNORECASE = RegexFlag.IGNORECASE
    L = RegexFlag.L
    LOCALE = RegexFlag.LOCALE
    M = RegexFlag.M
    MULTILINE = RegexFlag.MULTILINE
    S = RegexFlag.S
    DOTALL = RegexFlag.DOTALL
    X = RegexFlag.X
    VERBOSE = RegexFlag.VERBOSE
    U = RegexFlag.U
    UNICODE = RegexFlag.UNICODE
    T = RegexFlag.T
    TEMPLATE = RegexFlag.TEMPLATE
    _FlagsType = Union[int, RegexFlag]
else:
    A: int
    ASCII: int
    DEBUG: int
    I: int
    IGNORECASE: int
    L: int
    LOCALE: int
    M: int
    MULTILINE: int
    S: int
    DOTALL: int
    X: int
    VERBOSE: int
    U: int
    UNICODE: int
    T: int
    TEMPLATE: int
    _FlagsType = int

3. BeautifulSoup数据清洗

3.1 先来看一个小例子

    1. 首先新建一个test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>你好Beautiful</title>
</head>
<body>
    <p id="python">
        <a href='/index.html'>python</a>
        BeautifulSoup的使用
    </p>
    <p class="myClass">
        <a href='https://www.baidu.com'>这个一个指向百度的URL</a>
    </p>
</body>
</html>
    1. 使用BeautifulSoup + html5lib
# 导入BeautifulSoup
from bs4 import BeautifulSoup

# 读取html文件
f = open('./test.html','r',encoding='utf-8')
html = f.read()
f.close()

# 记得先安装包          -> python -m pip install html5lib
soup = BeautifulSoup(html, 'html5lib')

print('html_title=', soup.title.getText())

print('----------------')

p = soup.find('p', id='python')
print('id=python-->\\n', p)

print('================')
all_p = soup.find_all('p')

for i, k in enumerate(all_p):
    print('the ' + str(i+1) + ' p is ' + k.getText())


  • 结果
    在这里插入图片描述
  • 通过例子可以发现beautifulSoup的使用极其简单,后面我们再深入学习一下下。

3.2 详细用法 (依赖前面给的test.html

3.2.1 获取标签

  1. 获取单个标签(返回符合条件的第一个标签)

直接通过beautifulSoup对象.来获取()

  1. 获取同一类型的所有标签

beautifulSoup.find_all('tag') -> tag为要获取的标签名字

  • coding
# 导入BeautifulSoup
from bs4 import BeautifulSoup

# 读取html文件
f = open('./test.html','r',encoding='utf-8')
html = f.read()
f.close()

# 记得现安装包          -> python -m pip install html5lib
soup = BeautifulSoup(html, 'html5lib')

# 获取标签
# print(soup.title)
''' <title>你好Beautiful</title> '''

# 获取标签里的文本
# print('html_title=', soup.title.getText())
""" html_title= 你好Beautiful """

# 返回第一个符合条件的标签
# print(soup.body.p)
""" <p id="python">
        <a href="/index.html">python</a>
        BeautifulSoup的使用
    </p> 
"""

# print(type(soup.body.p))
# <class 'bs4.element.Tag'>
# print(type( str (soup.body.p)))
# <class 'str'>

# 2. 获取所有标签
# all_p = soup.find_all('p')
# print(all_p)
"""
[
    <p id="python">
        <a href="/index.html">python</a>
        BeautifulSoup的使用
    </p>, <p class="myClass">
        <a href="https://www.baidu.com">这个一个指向百度的URL</a>
    </p>
]

"""



# for i, k in enumerate(all_p):
#     print('the ' + str(i+1) + ' p is ' + k.getText())

"""
the 1 p is
        python
        BeautifulSoup的使用

the 2 p is
        这个一个指向百度的URL
"""



3.2.2 获取标签属性

# 导入BeautifulSoup
from bs4 import BeautifulSoup

# 读取html文件
f = open('./test.html','r',encoding='utf-8')
html = f.read()
f.close()

# 记得现安装包          -> python -m pip install html5lib
soup = BeautifulSoup(html, 'html5lib')


# 2. 获取标签属性

print(soup.p['id'])
# python

print(soup.a.href, '\\n====\\n',soup.a['href'])

'''结果 ->注意要使用中括号加字符串的形式
None
====
 /index.html
'''




3.2.3 精准查找

获取id='xx'的标签
beautifulSoup对象.find_all ( 'tag', id='xx' )
beautifulSoup对象.find_all ( 'tag', class='xx' )

  • test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>你好Beautiful</title>
</head>
<body>
    <p id="python" class="myClass">
        <a href='/index.html'>python</a>
        BeautifulSoup的使用
    </p>
    <p class="myClass" id='hello'>
        <a href='https://www.baidu.com'>这个一个指向百度的URL</a以上是关于爬虫基本工系列之--数据获取的主要内容,如果未能解决你的问题,请参考以下文章

爬虫基本工系列之--数据存储

爬虫系列

爬虫系列之解决动态数据获取

scrapy爬虫系列之三--爬取图片保存到本地及日志的基本用法

浅谈系列之跨站脚本工了个鸡(XSS)

大数据采集:爬虫框架之WebMagic的基本使用