python爬虫笔记第二章

前言

你好! 这是基于b站2021年路飞学成爬虫教程的python爬虫学习笔记,主要是方便作者复习和回顾课程内容。
已经发布了第一章和第二章,可以在主页中查看。

01数据解析概述

  1. re解析
  2. bs4解析
  3. xpath解析

02正则表达式

正则的语法: 使⽤元字符进⾏排列组合⽤来匹配字符串 在线测试正则表达式https://tool.oschina.net/regex/

元字符

. 匹配除换⾏符以外的任意字符
\w 匹配字⺟或数字或下划线
\s 匹配任意的空⽩符
\d 匹配数字
\n 匹配⼀个换⾏符
\t 匹配⼀个制表符

^ 匹配字符串的开始
$ 匹配字符串的结尾

\W 匹配⾮字⺟或数字或下划线
\D 匹配⾮数字
\S 匹配⾮空⽩符
a|b 匹配字符a或字符b
() 匹配括号内的表达式,也表示⼀个组
[…] 匹配字符组中的字符
[^…] 匹配除了字符组中字符的所有字符

量词

*重复零次或更多次
+重复⼀次或更多次
? 重复零次或⼀次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

贪婪匹配和惰性匹配

.* 贪婪匹配

.* ?惰性匹配 (让*尽可能少出现的结果

注意:.*? 表示尽可能少的匹配, .*表示尽可能多的匹配

03 re模块

03_01 findall

查找所有,返回list

import re
lst = re.findall(r"\d+", "我的电话号是:10086,我女朋友电话是10010")
print(lst)
#['10086', '10010']

03_02 search

search 会进⾏匹配. 但是如果匹配到了第⼀个结果. 就会返回这个结果. 如果匹配不上search返回的则是None

ret = re.search(r'\d', '5点之前. 你要给我5000万').group()
print(ret) # 5

s = re.search(r"\d+", "我的电话号是:10086,我女朋友电话是10010")
print(s.group())# 10086

03_03 match

只能从字符串的开头进⾏匹配

#从头匹配
ss = re.match(r"\d+", "10086,我女朋友电话是10010")
print(ss.group())

03_04 finditer

finditer, 和findall差不多. 只不过这时返回的是迭代器(重点)

# finditer: 匹配字符串中所有的内容[返回的是迭代器], 从迭代器中拿到内容需要.group()
it = re.finditer(r"\d+", "我的电话号是:10086, 我女朋友的电话是:10010")
for i in it:
    print(i.group())
#10086
#10010

03_05 compile

compile() 可以将⼀个⻓⻓的正则进⾏预加载. ⽅便后⾯的使⽤

#预加载正则表达式
obj = re.compile(r"\d+")

ret = obj.finditer("我的电话号是:10086,我女朋友电话是10010")
for it in ret:
    print(it.group())

03_06 提取正则表达式中的单独内容

单独获取到正则中的具体内容可以给分组起名字.
(?P<分组名字>正则) 可以单独从正则匹配的内容中进一步提取内容

s = """
<div class='jay'><span id='1'>郭麒麟</span></div>
<div class='jj'><span id='2'>宋轶</span></div>
<div class='jolin'><span id='3'>大聪明</span></div>
<div class='sylar'><span id='4'>范思哲</span></div>
<div class='tory'><span id='5'>胡说八道</span></div>
"""

# (?P<分组名字>正则) 可以单独从正则匹配的内容中进一步提取内容
obj = re.compile(r"<div class='.*?'><span id='(?P<id>\d+)'>(?P<name>.*?)</span></div>", re.S)
# re.S: 让.能匹配换行符
result = obj.finditer(s)
for it in result:
    print(it.group("name"))
    print(it.group("id"))

04 运用request和re的两个爬虫案例

0401 爬取豆瓣top250排行榜

需求:在https://movie.douban.com/top250上抓取"电影名称",“上映年份”,“评分”,"评分⼈数"四项内容。

# 拿到页面源代码.   requests
# 通过re来提取想要的有效信息  re
import requests
import re
import csv

url = "https://movie.douban.com/top250"
headers = {
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36"
}
resp = requests.get(url, headers=headers)
page_content = resp.text

# 解析数据
obj = re.compile(r'<li>.*?<div class="item">.*?<span class="title">(?P<name>.*?)'
                 r'</span>.*?<p class="">.*?<br>(?P<year>.*?)&nbsp.*?<span '
                 r'class="rating_num" property="v:average">(?P<score>.*?)</span>.*?'
                 r'<span>(?P<num>.*?)人评价</span>', re.S)
# 开始匹配
result = obj.finditer(page_content)
f = open("data.csv", mode="w")
csvwriter = csv.writer(f)
for it in result:
    # print(it.group("name"))
    # print(it.group("score"))
    # print(it.group("num"))
    # print(it.group("year").strip())
    dic = it.groupdict()
    dic['year'] = dic['year'].strip()
    csvwriter.writerow(dic.values())

f.close()
print("over!")

0402爬取电影天堂2020必看榜

需求:在 "https://www.dytt89.com/"上,

  1. 定位到2020必看片
  2. 从2020必看片中提取到子页面的链接地址
  3. 请求子页面的链接地址. 拿到我们想要的下载地址…

预备知识
HTML中a标签表示超链接
实例:指向 w3school 的超链接:

<a href="http://www.w3school.com.cn">W3School</a>

1.定位到2020必看片

import requests
import re

domain = "https://www.dytt89.com/"
resp = requests.get(domain, verify=False)#去掉安全验证

#'str' object is not callable。因为默认字节转换成utf-8,而网站编码可能不是utf-8
#查看网页源代码,发现charset=gb2312
resp.encoding = 'gb2312'
#print(resp.text)
# 拿到ul里面的li
obj1 = re.compile(r"2020必看热片.*?<ul>(?P<ul>.*?)</ul>", re.S)

result1 = obj1.finditer(resp.text)
for it in result1:
    ul = it.group('ul')
#    print(ul)

2.从2020必看片中提取到子页面的链接地址

# 1. 定位到2020必看片
# 2. 从2020必看片中提取到子页面的链接地址
# 3. 请求子页面的链接地址. 拿到我们想要的下载地址....
import requests
import re

domain = "https://www.dytt89.com/"
resp = requests.get(domain, verify=False)#去掉安全验证

#'str' object is not callable。因为默认字节转换成utf-8,而网站编码可能不是utf-8
#查看网页源代码,发现charset=gb2312
resp.encoding = 'gb2312'
#print(resp.text)
# 拿到ul里面的li
obj1 = re.compile(r"2020必看热片.*?<ul>(?P<ul>.*?)</ul>", re.S)
obj2 = re.compile(r"<a href='(?P<href>.*?)'", re.S)

result1 = obj1.finditer(resp.text)
for it in result1:
    ul = it.group('ul')
#    print(ul)

    result2 = obj2.finditer(ul)
    for itt in result2:
        print(itt.group('href'))

此时的结果是:
在这里插入图片描述
很明显不是完整的网址。所以需要子页面链接与domain链接拼接到一起。将代码改如下:(子页面链接与domain链接拼接到一起,并把子页面链接保存在列表里)

result1 = obj1.finditer(resp.text)
child_href_list= []
for it in result1:
    ul = it.group('ul')
#    print(ul)

    result2 = obj2.finditer(ul)
    for itt in result2:
#        print(itt.group('href'))
        child_href = domain + itt.group('href').strip("/")# 拼接子页面的url地址:  域名 + 子页面地址
        child_href_list.append(child_href)  # 把子页面链接保存起来

最后请求子页面的链接地址. 拿到我们想要的下载地址。
全部代码如下:

# 1. 定位到2020必看片
# 2. 从2020必看片中提取到子页面的链接地址
# 3. 请求子页面的链接地址. 拿到我们想要的下载地址....
import requests
import re

domain = "https://www.dytt89.com/"
resp = requests.get(domain, verify=False)#去掉安全验证

#'str' object is not callable。因为默认字节转换成utf-8,而网站编码可能不是utf-8
#查看网页源代码,发现charset=gb2312
resp.encoding = 'gb2312'
#print(resp.text)
# 拿到ul里面的li
obj1 = re.compile(r"2020必看热片.*?<ul>(?P<ul>.*?)</ul>", re.S)
obj2 = re.compile(r"<a href='(?P<href>.*?)'", re.S)
obj3 = re.compile(r'◎片  名(?P<movie>.*?)<br />.*?<td'
                  r' style="WORD-WRAP: break-word" bgcolor="#fdfddf"><a href="(?P<download>.*?)">', re.S)
result1 = obj1.finditer(resp.text)
child_href_list= []
for it in result1:
    ul = it.group('ul')
#    print(ul)

    result2 = obj2.finditer(ul)
    for itt in result2:
#        print(itt.group('href'))
        child_href = domain + itt.group('href').strip("/")# 拼接子页面的url地址:  域名 + 子页面地址
        child_href_list.append(child_href)  # 把子页面链接保存起来
#提取子页面内容
for href in child_href_list:
    child_resp = requests.get(href, verify=False)
    child_resp.encoding = 'gb2312'
#   print(child_resp.text)
    result3 = obj3.search(child_resp.text)
    print(result3.group("movie"))
    print(result3.group("download"))

#    break#测试用

05 BS4解析——html语法

HTML(Hyper Text Markup Language)超⽂本标记语⾔, 是我们编写⽹⻚的最基本也是最核⼼的⼀种语⾔. 其语法规则就是⽤不同的标签对⽹⻚上的内容进⾏标记, 从⽽使⽹⻚显示出不同的展示效果.

HTML标签

h1: ⼀级标题
h2: ⼆级标题
p: 段落
font: 字体(被废弃了, 但能⽤)
body: 主体

HTML属性

<标签 属性="" 属性="">
    被标记的内容
</标签>

<body text="green" bgcolor="#eee">
    你看我的颜⾊. 贼健康
</body>

06 bs4解析

1.bs4模块安装
参考教程

2 尝试爬取http://www.xinfadi.com.cn/marketanalysis/0/list/1.shtml

预备知识:
HTML中每一行是一个tr,每一列叫一个td。

# 1. 拿到页面源代码
# 2. 使用bs4进行解析. 拿到数据
import requests
from bs4 import BeautifulSoup
import csv

url = "http://www.xinfadi.com.cn/marketanalysis/0/list/1.shtml"
resp = requests.get(url)
#print(resp.text)

f = open("菜价.csv", "w")
csvwriter = csv.writer(f)

#解析数据
# 1. 把页面源代码交给BeautifulSoup进行处理, 生成bs对象
page = BeautifulSoup(resp.text, "html.parser")# 若不指定html解析器,会出现warning
# 2. 从bs对象中查找数据
# find:找一个(标签, 属性=值)
# find_all:找全部(标签, 属性=值)
#table = page.find("table", class_="hq_table")
table = page.find("table", attrs={"class":"hq_table"})
# 和上一行是一个意思. 此时可以避免class。因为class是python的关键字。
#print(table)
trs = table.find_all("tr")[1:]
#每一行是一个tr,每一列叫一个td
for tr in trs: # 每一行
    tds = tr.find_all("td") # 拿到每行中的所有td
    name = tds[0].text  # .text 表示拿到被标签标记的内容
    low = tds[1].text  # .text 表示拿到被标签标记的内容
    avg = tds[2].text  # .text 表示拿到被标签标记的内容
    high = tds[3].text  # .text 表示拿到被标签标记的内容
    gui = tds[4].text  # .text 表示拿到被标签标记的内容
    kind = tds[5].text  # .text 表示拿到被标签标记的内容
    date = tds[6].text  # .text 表示拿到被标签标记的内容
#    print(name, low, avg, high, gui, kind, date)
    csvwriter.writerow([name, low, avg, high, gui, kind, date])

f.close()
resp.close() #爬完要关掉resp
print("over!")

07 bs4案例解析:爬取唯美壁纸

对于https://www.umei.cc/bizhitupian/weimeibizhi/。需求如下:
1.拿到主页面的源代码. 然后提取到子页面的链接地址, href
2.通过href拿到子页面的内容. 从子页面中找到图片的下载地址 img -> src
3.下载图片

# 1.拿到主页面的源代码. 然后提取到子页面的链接地址, href
# 2.通过href拿到子页面的内容. 从子页面中找到图片的下载地址 img -> src
# 3.下载图片
import requests
from bs4 import BeautifulSoup
import time

url = "https://www.umei.cc/bizhitupian/weimeibizhi/"
resp = requests.get(url)
resp.encoding = 'utf-8' # 处理乱码 charset=utf-8
#print(resp.text)
# 把源代码交给bs
main_page = BeautifulSoup(resp.text, "html.parser")
alist = main_page.find("div", class_="TypeList").find_all("a")
#print(alist)
for a in alist:
    # 拿到子页面的源代码
    href = a.get('href')
    child_page_resp = requests.get(href)
    child_page_resp.encoding = 'utf-8'
    child_page_text = child_page_resp.text
    # 从子页面中拿到图片的下载路径
    child_page = BeautifulSoup(child_page_text, "html.parser")
    p = child_page.find("p", align="center") #一般通过class或id定位,但是这个网页源代码align="center是唯一的
    img = p.find("img")
    src = img.get("src")
    #下载图片
    img_resp = requests.get(src)
    img_resp.content #拿到的是字节,但是写进文件里就成了图片
    img_name = src.split("/")[-1]# 拿到url中的最后一个/以后的内容
    with open("img/"+img_name, "wb") as f:
        f.write(img_resp.content)
    print("over!!!", img_name)
    time.sleep(1)

f.close()
resp.close() #爬完要关掉resp
print("all over!!!")

08 xpath解析

XPath是⼀⻔在 XML ⽂档中查找信息的语⾔. XPath可⽤来在 XML⽂档中对元素和属性进⾏遍历. ⽽我们熟知的HTML恰巧属于XML的⼀个⼦集. 所以完全可以⽤xpath去查找html中的内容.

  1. book, id, name, price…都被称为节点.
  2. Id, name, price, author被称为book的⼦节点
  3. book被称为id, name, price, author的⽗节点
  4. id, name, price,author被称为同胞节点

⽤法:

  1. 将要解析的html内容构造出etree对象.
  2. 使⽤etree对象的xpath()⽅法配合xpath表达式来完成对数据的提取

xpath解析1

# xpath 是在XML文档中搜索内容的一门语言
# html是xml的一个子集

# xpath解析
from lxml import etree

xml = """
<book>
    <id>1</id>
    <name>野花遍地香</name>
    <price>1.23</price>
    <nick>臭豆腐</nick>
    <author>
        <nick id="10086">周大强</nick>
        <nick id="10010">周芷若</nick>
        <nick class="joy">周杰伦</nick>
        <nick class="jolin">蔡依林</nick>
        <div>
            <nick>热热热热热1</nick>
        </div>
        <span>
            <nick>热热热热热2</nick>
        </span>
    </author>

    <partner>
        <nick id="ppc">胖胖陈</nick>
        <nick id="ppbc">胖胖不陈</nick>
    </partner>
</book>
"""

tree = etree.XML(xml)
# /表示层级关系. 第一个/是根节点
#result = tree.xpath("/book")
#result = tree.xpath("/book/name")
#result = tree.xpath("/book/name/text()")# text() 拿文本
#result = tree.xpath("/book/author/nick/text()")
#result = tree.xpath("/book/author//nick/text()")  # // 后代
#result = tree.xpath("/book/author/*/nick/text()")  # * 任意的节点. 通配符
result = tree.xpath("/book//nick/text()")
print(result)

xpath解析2

预备知识:在网页中右键-检查:
在这里插入图片描述
先准备一个网页b.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Title</title>
    </head>
    <body>
        <ul>
            <li><a href="http://www.baidu.com">百度</a></li>
            <li><a href="http://www.google.com">谷歌</a></li>
            <li><a href="http://www.sogou.com">搜狗</a></li>
        </ul>
        <ol>
            <li><a href="feiji">飞机</a></li>
            <li><a href="dapao">大炮</a></li>
            <li><a href="huoche">火车</a></li>
        </ol>
        <div class="job">李嘉诚</div>
        <div class="common">胡辣汤</div>
    </body>
</html>

用xpath解析:

from lxml import etree

tree = etree.parse("b.html")
#result = tree.xpath('/html')
#result = tree.xpath('/html/body/ul/li/a/text()')
#result = tree.xpath('/html/body/ul/li[1]/a/text()')# xpath的顺序是从1开始数的, []表示索引
#result = tree.xpath('/html/body/ol/li//a[@href="dapao"]/text()')
# [@xxx=xxx] 属性的筛选


#print(result)

# ol_li_list = tree.xpath("/html/body/ol/li")
# for li in ol_li_list:
#     result = li.xpath("./a/text()")# ./表示在li中继续去寻找. 相对查找
#     print(result)
#     result2 = li.xpath("./a//@href")# 拿到属性值: //@属性
#     print(result2)

print(tree.xpath("/html/body/ul/li/a//@href"))
print(tree.xpath("/html/body/div[1]/text()"))
print(tree.xpath("/html/body/ol/li/a/text()"))

09 案例:xpath解析猪八戒网

需求:对于https://beijing.zbj.com/search/f/?type=new&kw=saas,拿到页面源代码,提取和解析数据

# 拿到页面源代码
# 提取和解析数据
import requests
from lxml import etree

url = "https://beijing.zbj.com/search/f/?type=new&kw=saas"
resp = requests.get(url)
#print(resp.text)

# 解析
html = etree.HTML(resp.text)
# 拿到每一个服务商的div
divs = html.xpath("/html/body/div[6]/div/div/div[2]/div[4]/div[1]/div")
for div in divs:
    price = div.xpath("./div/div/a[1]/div[2]/div[1]/span[1]/text()")[0].strip("¥")
    title = "SaaS".join(div.xpath("./div/div/a[1]/div[2]/div[2]/p/text()"))
    com_name = div.xpath("./div/div/a[2]/div[1]/p/text()")[0]
    location = div.xpath("./div/div/a[2]/div[1]/div/span/text()")[0]
    print(price)
    print(title)
    print(com_name)
    print(location)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值