小言_互联网的博客

【Python】爬虫入门强烈推荐系列二

391人阅读  评论(0)

在系列一中,我们重点学习了网页的基本组成与网页代码的简单分析,并且学习了requests库的实战操作。requests是python实现的最简单易用的HTTP库,因此强烈建议爬虫使用requests。系列一链接:【Python】爬虫入门强烈推荐系列一

解析和提取

我们通过requests可以很轻松地就获得网页上的所有内容,但是这些内容往往会夹杂着许多我们不需要的东西,因此我们需要解析和提取 HTML 数据。
本系列重点围绕三个库进行解析,按重要程度排名 分别先后进行介绍。重点介绍正则表达式,效率高,但复杂,在学术界和工业场景应用最多,建议硬啃,真的理解不了的新手可倒着看~。

正则表达式 re

re的定义

正则表达式是什么?

  • 正则表达式是用来简洁表达一组字符串的表达式
  • 正则表达式是一种通用的字符串表达框架
  • 正则表达式是一种针对字符串表达“简洁”和“特征”思想的工具
  • 简单来说,它是一种模式,把该模式应用于文本匹配,然后获取符合该模式的内容,当然,这些内容就是我们所需要的。

使用正则表达式可以干什么?

  • 测试字符串内的模式。
    例如,可以测试输入字符串,以查看字符串内是否出现电话号码模式或信用卡号码模式。这称为数据验证。
  • 替换文本。
    可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它。
  • 基于模式匹配从字符串中提取子字符串。
    可以查找文档内或输入域内特定的文本;可以使用正则表达式来搜索和替换标记。

re的语法

正则表达式语法由字符和操作符构成:

常用操作符

符号 作用
. 表示任何单个字符
[ ] 字符集,对单个字符给出取值范围 ,如[abc]表示a、b、c,[a‐z]表示a到z单个字符
[^ ] 非字符集,对单个字符给出排除范围 ,如[^abc]表示非a或b或c的单个字符
* 前一个字符0次或无限次扩展,如abc* 表示 ab、abc、abcc、abccc等
+ 前一个字符1次或无限次扩展 ,如abc+ 表示 abc、abcc、abccc等
? 前一个字符0次或1次扩展 ,如abc? 表示 ab、abc
| 左右表达式任意一个 ,如abc
{m} 扩展前一个字符m次 ,如ab{2}c表示abbc
{m,n} 扩展前一个字符m至n次(含n) ,如ab{1,2}c表示abc、abbc
^ 匹配字符串开头 ,如^abc表示abc且在一个字符串的开头
$ 匹配字符串结尾 ,如abc$表示abc且在一个字符串的结尾
( ) 分组标记,内部只能使用
\d 数字,等价于[0‐9]
\w 单词字符,等价于[A‐Za‐z0‐9_]

re的使用

  • 调用方式:import re
  • re库采用raw string类型表示正则表达式,表示为:r’text’,raw string是不包含对转义符再次转义的字符串

主要功能函数:

函数 功能
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(pattern, string, flags=0) 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象
re.sub(pattern, repl, string, count=0, flags=0) 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串

参数解析:

pattern:指的是正则表达式;
string:指的是我们需要进行操作的字符串;
flags : 正则表达式使用时的控制标记:

  • re.I --> re.IGNORECASE : 忽略正则表达式的大小写,[A‐Z]能够匹配小写字符
  • re.M --> re.MULTILINE : 正则表达式中的^操作符能够将给定字符串的每行当作匹配开始
  • re.S --> re.DOTALL : 正则表达式中的.操作符能够匹配所有字符,默认匹配除换行外的所有字符

re的对象

生成re对象:

regex = re.compile(pattern, flags=0):将正则表达式的字符串形式编译成正则表达式对象

与直接调用函数进行比较:

1、re对象
content = 'Hello, I am Wuijekd, from Guangdong.'
regex = re.compile('\w*')
sentence = regex.match(content)
print(sentence.group())

[Output]:Hello

2、直接调用函数
sentence = re.match('\w*',content)
print(sentence.group())

[Output]:Hello

re 库的贪婪匹配和最小匹配
.* Re库默认采用贪婪匹配,即输出匹配最长的子串
*? 只要长度输出可能不同的,都可以通过在操作符后增加?变成最小匹配

content = 'Hello, I am Wuijekd, from Guangdong.'
sentence = re.match('\w.*',content)
print(sentence.group())

[Output]:Hello, I am Wuijekd, from Guangdong.

sentence = re.match('\w.?',content)
print(sentence.group())

[Output]:He

re的实战(淘宝网)

目标网站:淘宝商品比价定向爬虫

本次任务不是爬取简单的公开网站,而是需要进行登录的网站。

user-agent:浏览器请求头,伪装成浏览器访问。
cookie:某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息。

获取cookie

因此requests的头参数要加上cookie,cookie获取方式如下:
一、打开网站,登录账号,然后F12打开开发者模式

二、点击控制台Console,输入 document.cookie
会打印出cookie,我认为这是最简单的方法啦~

三、但是以上获得的cookie并不能正常登录,应该是被加密过了。接下来介绍一种可以的方法~

开发者模式——》Network——》Doc

一般都会有文件显示的,没有文件(如上图所示)也不用担心,可以使用Command+R 加载出来

注意,看Domain,要选对域名。
四、获取cookie

完整代码
# 导入包
import requests
import re

def getHTMLText(url):
    """
    请求获取html,(字符串)
    :param url: 爬取网址
    :return: 字符串
    """
    try:
        # 添加头信息,
        kv = {
            'cookie':"_samesite_flag_=true; cookie2=14eae9f8d1b1482a3024a8a0168082b8; t=6be116d8f4642ef31ff898e55ecb9439; _tb_token_=31d8fbb4e83f1; enc=s7EH5X4%2FGgqxsjL7VtaKey8phN9RXGhbGkKEyZpmr68mrE%2FnNHZdELKNJr6y22k%2FbxIXQYvdHcxSZpwQJWgPZQ%3D%3D; thw=cn; hng=CN%7Czh-CN%7CCNY%7C156; alitrackid=localhost; cna=nibkFqjYak0CAXeGZypvKU/b; sgcookie=EKd%2BRDRDPF%2FJG1XMg7Okz; unb=3981685338; uc3=vt3=F8dBxGR1T9WfwR2Of8Q%3D&lg2=URm48syIIVrSKA%3D%3D&nk2=3HLTelMH&id2=UNk%2FSaQ%2FYRgMow%3D%3D; csg=8d5f24da; lgc=%5Cu79D1%5Cu8FBEgg; cookie17=UNk%2FSaQ%2FYRgMow%3D%3D; dnk=%5Cu79D1%5Cu8FBEgg; skt=48c3c62d91f0d8b0; existShop=MTU4NzY2ODEzNA%3D%3D; uc4=nk4=0%403gaHD9GnaeORT92znufTRM8%3D&id4=0%40Ug41SKOjzmPgtWhljS4ncL0lYc5Y; tracknick=%5Cu79D1%5Cu8FBEgg; _cc_=W5iHLLyFfA%3D%3D; _l_g_=Ug%3D%3D; sg=g89; _nk_=%5Cu79D1%5Cu8FBEgg; cookie1=AiHMAoCGguEVoGsBWLpm8hBz2m%2FdybS6HlqpmxkrSsY%3D; tfstk=cTrhBBGvTfOfMsUQhpiQe6r-1jQAaSO-izzgbkWu4tTneiqZLsv67Eu9P1cQtuL5.; JSESSIONID=F301BA29D60E24F25DFC2CD06F7FAE99; lastalitrackid=login.taobao.com; mt=ci=7_1; v=0; l=eBQcoDLcQ2sy_ghQBO5whurza77t0QOf1sPzaNbMiIHca1RR6asdCNQcc5v6JdtjgtCX6eKPS7AArRdxbn4Nw14Ki2trCyCuwxvO",
            'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36'
        }
        r = requests.get(url, timeout=30, headers=kv)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return "爬取失败"

def parsePage(glist, html):
    '''
    解析网页,搜索需要的信息
    :param glist: 列表作为存储容器
    :param html: 由getHTMLText()得到的
    :return: 商品信息的列表
    '''
    try:
        # 使用正则表达式提取信息
        price_list = re.findall(r'\"view_price\"\:\"[\d\.]*\"', html)
        name_list = re.findall(r'\"raw_title\"\:\".*?\"', html)
        for i in range(len(price_list)):
            price = eval(price_list[i].split(":")[1])  #eval()在此可以去掉""
            name = eval(name_list[i].split(":")[1])
            glist.append([price, name])
    except:
        print("解析失败")

def printGoodList(glist):
    tplt = "{0:^4}\t{1:^6}\t{2:^10}"
    print(tplt.format("序号", "商品价格", "商品名称"))
    count = 0
    for g in glist:
        count = count + 1
        print(tplt.format(count, g[0], g[1]))

# 根据页面url的变化寻找规律,构建爬取url
goods_name = "书包"  # 搜索商品类型
start_url = "https://s.taobao.com/search?q=" + goods_name
info_list = []
page = 3  # 爬取页面数量

count = 0
for i in range(page):
    count += 1
    try:
        url = start_url + "&s=" + str(44 * i)
        html = getHTMLText(url)  # 爬取url
        parsePage(info_list, html) #解析HTML和爬取内容
        print("\r爬取页面当前进度: {:.2f}%".format(count * 100 / page), end="")  # 显示进度条
    except:
        continue

printGoodList(info_list)
结果展示

Xpath

Xpath的定义

Xpath是什么?

  • Xpath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。

XML和Xpath

  • XML文档是被作为节点树来对待的
  • 在Xpath中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点
  • Xpath使用路径表达式在XML文档中选取节点。节点是通过沿着路径选取的

HTML的转换

我们知道requests获得的都是HTML,但Xpath是专门处理XML的,因此我们得先对HTML进行转换。

from lxml import etree

req = requests.get(url)
html = req.text
tree = etree.HTML(html)

XML的读取

因为XML是被当作树来对待,是一层层的去寻找我们需要的内容的。
接下来,就是使用Xpath进行提取~

用户名称:tree.xpath(//div[@class=“auth”]/a/text())

回复内容:tree.xpath(//td[@class=“postbody”]) 

Xpath实战(丁香园)

目标网站:丁香园

from lxml import etree
import requests

url = "http://www.dxy.cn/bbs/thread/626626#626626"
tree = etree.HTML(requests.get(url).text) 

user = tree.xpath('//div[@class="auth"]/a/text()')
content = tree.xpath('//td[@class="postbody"]')

results = []
for i in range(0, len(user)):
    # print(user[i].strip()+":"+content[i].xpath('string(.)').strip())
    # print("*"*80)
    # 因为回复内容中有换行等标签,所以需要用string()来获取数据
    results.append(user[i].strip() + ":  " + content[i].xpath('string(.)').strip())

# 打印爬取的结果
for i,result in zip(range(0, len(user)),results):
    print("user"+ str(i+1) + "-" + result)
    print("*"*100)

结果展示:

Beautiful Soup

Beautiful Soup的定义

Beautiful Soup是什么?

  • Beautiful Soup 是一个HTML/XML 的解析器,主要用于解析和提取 HTML/XML 数据。

Beautiful Soup直观了解

  • 它虽然也可以用树去进行理解,但它和Xpath不一样,它可以认为是一种规则的数据结构,接下来我们直接使用它对HTML代码进行分析。
# 导入bs4库
from bs4 import BeautifulSoup
import requests # 抓取页面

r = requests.get('https://python123.io/ws/demo.html') # Demo网址
print(r.text)  # 抓取的数据


可以看到,HTML代码整洁,一层层的,符合数据结构的思想~

# 解析HTML页面
soup = BeautifulSoup(demo, 'html.parser')  # 抓取的页面数据;bs4的解析器
# 有层次感的输出解析后的HTML页面
print(soup.prettify())

HTML内容遍历

HTML基本格式:<>…</>构成了所属关系,形成了标签的树形结构

  • 标签树的下行遍历
    .contents 子节点的列表,将所有儿子节点存入列表
    .children 子节点的迭代类型,与.contents类似,用于循环遍历儿子节点
    .descendants 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历
  • 标签树的上行遍
    .parent 节点的父亲标签
    .parents 节点先辈标签的迭代类型,用于循环遍历先辈节点
  • 标签树的平行遍历
    .next_sibling 返回按照HTML文本顺序的下一个平行节点标签
    .previous_sibling 返回按照HTML文本顺序的上一个平行节点标签
    .next_siblings 迭代类型,返回按照HTML文本顺序的后续所有平行节点标签
    .previous_siblings 迭代类型,返回按照HTML文本顺序的前续所有平行节点标签

HTML内容查找

  • <>.find_all(name, attrs, recursive, string, **kwargs)
  • 参数:
    ∙ name : 对标签名称的检索字符串
    ∙ attrs: 对标签属性值的检索字符串,可标注属性检索
    ∙ recursive: 是否对子孙全部检索,默认True
    ∙ string: <>…</>中字符串区域的检索字符串

以上内容都属于数据结构里的内容,一行一行代码输出更有利于学习该库~

实战(最好大学)

目标网站:最好大学网

# 导入库
import requests
from bs4 import BeautifulSoup
import bs4

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 fillUnivList(ulist, html):
    soup = BeautifulSoup(html, "html.parser")
    for tr in soup.find('tbody').children: 
        if isinstance(tr, bs4.element.Tag):
            tds = tr('td')
            # 根据实际提取需要的内容,
            ulist.append([tds[0].string, tds[1].string, tds[3].string])  

# 对中英文混排输出问题进行优化:对format(),设定宽度和添加参数chr(12288)
def printUnivList(ulist, num=20):
    tplt = "{0:^10}\t{1:{3}^10}\t{2:^10}"
    print(tplt.format('排名', '学校名称', '总分', chr(12288)))
    for i in range(num):
        u = ulist[i]
        print(tplt.format(u[0], u[1], u[2], chr(12288)))

u_info = [] # 存储爬取结果的容器
url = 'http://www.zuihaodaxue.cn/zuihaodaxuepaiming2019.html'


html = getHTMLText(url)
fillUnivList(u_info, html) # 爬取
printUnivList(u_info, num=30) # 打印输出30个信息
  • getHTMLText:从网络上获取大学排名网页内容
  • fillUnivList:提取网页内容中信息到合适的数据结构(二维数组)
  • printUnivList:利用数据结构展示并输出结果

小结

三种不同方法的比较:

re Xpath bs4
安装 内置 第三方 第三方
语法 正则 路径匹配 面向对象
难度 困难 较困难 简单
效率 最高 适中 最低
  • 大数据时代,效率为王,请选择正则表达式re。
  • 后续再补充一个某度文库VIP文档爬取实战。
  • 完。

转载:https://blog.csdn.net/weixin_43999137/article/details/105721010
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场