您现在的位置是:首页 >其他 >Python_正则_爬虫_子网划分程序网站首页其他

Python_正则_爬虫_子网划分程序

ch# 2023-06-15 04:00:02
简介Python_正则_爬虫_子网划分程序

正则表达式

正则表达式在Python中,它被内嵌在了re模块里面

. 通配符 代表匹配除 后的任意字符。

^ 起始锚定符 代表被匹配的字符串必须以某个子串开头,只检测开头。

$ 结束锚定符 代表被匹配的字符串必须以某个子串结尾,只检测结尾。 

不论成功或者失败都会返回      

* 重复符 代表可以取0到无穷位 

+ 重复符 代表可以取1到无穷位

? 重复符 代表可以取0到1位 

{n} 重复符 精确匹配n个前面的表达式

{n,m} 重复符 代表匹配n到m次由前面的正则表达式定义的片段

上述(默认贪婪取值,可通过?取消贪婪模式)

取消贪婪模式就是取最小

*?就是0

+?就是1

??就是0

[] 字符集 其本身代表或的作用 [ab]代表a或者b。 ’

在字符集中上面的方法均失去原本含义。但 - ^ 可以在字符集中使用

[-] 字符集中的 - 号代表可以取从多少到多少区间的值。

[^] 字符集中的 ^ 号代表 非 的作用。比如[^0-9]就是说这一位数并非数字

[] 转义符  

d 匹配任何十进制数,它相当于在字符集中使用[0-9]

D 匹配任何非十进制数,它相当于在字符集中使用[^0-9]

s 匹配任何空白字符,它相当于在字符集中使用[ fv]

S 匹配任何非空白字符,它相当于在字符集中使用[^ fv]

w 匹配任何字母数字下划线字符,它相当于在字符集中使用[a-z A-Z 0-9]

W 匹配任何非字母数字下划线字符,它相当于在字符集中使用[a-z A-Z 0-9]

 匹配一个特殊字符边界,比如空格,&.#等(不常用)

A 匹配字符串开始(不常用)

 匹配字符串结束,如果存在换行则只匹配换行前的字符(不常用)

z 匹配字符串结束(不常用)

G 匹配最后匹配完成的位置(不常用)

匹配一个换行符(不常用)

匹配一个制表符(不常用)

f 匹配一个分页符(不常用)

转义字符

简易爬虫 

使用爬虫的时候不能使用代理或者vpn,因为使用代理可以帮助我们隐藏真实 IP 地址,从而保护个人隐私和安全,同时也可以绕过某些限制或者封锁。因此,很多非法爬虫程序都使用代理来进行访问。

爬取豆瓣top250电影网站的图片,这个是很久之前写的,发现网站的源代码有变化,要小改一下

豆瓣电影海报

import os
import re
import requests

url = 'https://movie.douban.com/top250'
# 正规一点的网站都有反爬机制,要加headers
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58'}
response = requests.get(url, headers=headers).text
# print(response)可以看看是否爬到源代码
# 对源代码进行人眼解析,看看那些是我们想要的
pattern1 = re.compile(r'alt="(.*)" src="(.*)" class')
response_img = pattern1.findall(response)
print(response_img)
pattern2 = re.compile('(.*?)"')

if not os.path.exists('D:电影图片'):
    os.mkdir(r'D:电影图片')

for title, image_url, in response_img:
    print(image_url)
    print(title)
    image_content = requests.get(image_url).content
    # print(image_content)

    with open('D:电影图片' + '\' + title + '.jpg', 'wb') as fw:
        print('正在保存《' + title + '》图片')
        fw.write(image_content)
 

​​​​​​拿到源代码需要人眼解析拿到自己想要的资源部分,需要花时间了解网页资源结构

例如网易云音乐文件url为

http://music.163.com/song/media/outer/url?id= + 歌曲id

网易云音乐热歌榜单

import os
import requests
import re

# 网易云网址,以热歌榜单链接为例
url = "https://movie.douban.com/celebrity/1045259/photos/"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58'}
response = requests.get(url, headers=headers).text

# 正则匹配网易云歌曲ID、歌曲名字
# <li><a href="/song?id=1968781675">一直很安静</a>
zip_data = re.findall('<li><a href="/song?id=(.*?)">(.*?)</a></li>', response)
print(zip_data)  # 打印查看


# 爬取音乐函数
def craw(music_url, music_name):
    music_data = requests.get(music_url, timeout=30).content
    # 新建一个文件夹
    if not os.path.exists('D:/网易云热歌'):
        os.mkdir('D:/网易云热歌')
    # 保存数据
    with open(f"D:/网易云热歌/{music_name}.mp3", 'wb') as f:
        f.write(music_data)


if __name__ == '__main__':
    for music_id, music_name in zip_data:
        print('开始下载歌曲{}...'.format(music_name))
        # 网易云外播链接
        music_url = "http://music.163.com/song/media/outer/url?id=" + music_id
        # print(music_url)
        craw(music_url, music_name)
        print('歌曲{}下载完成'.format(music_name))

qq音乐也有但是比较麻烦,有各种各样的序列,

http://dl.stream.qqmusic.qq.com/ + 很多序列 C400002202B43Cq4V4.m4a?guid=(全局唯一标识符?不确定)&vkey=(音频文件的验证密钥,用于验证用户是否有权访问该音频文件)&uin=(QQ号)&fromtag=(请求来源标识符,区分网页访问、移动端访问、第三方应用访问)

QQ 音乐版权多,可能会对 vkey 的计算方法进行更新和调整,因此在爬取数据时需要更新,以确保能够正确获取音频文件。因为我是会员进入访问的,可能不是会员有些歌曲就不能访问。

这个可能会泄露我的个人信息,就不放代码了,我没写,网上有:)

爬取图片(webp jpg后缀文件)函数

网站使用了WebP格式的图片来提高页面加载速度和降低带宽消耗,但是在Python爬虫中获取到的图片是服务器原始的格式,可能是JPEG格式的。这是因为Python爬虫只是获取了图片的二进制数据,而没有对图片格式进行转换。

import os
import requests
import re
 
# 网易云网址,以热歌榜单链接为例
url = "https://music.163.com/discover/toplist?id=3778678"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58'}
response = requests.get(url, headers=headers).text
 
# 正则匹配网易云歌曲ID、歌曲名字
# <li><a href="/song?id=1968781675">一直很安静</a>
zip_data = re.findall('<li><a href="/song?id=(.*?)">(.*?)</a></li>', response)
print(zip_data)  # 打印查看
 
 
# 爬取音乐函数
def craw(music_url, music_name):
    music_data = requests.get(music_url, timeout=30).content
    # 新建一个文件夹
    if not os.path.exists('D:/网易云热歌'):
        os.mkdir('D:/网易云热歌')
    # 保存数据
    with open(f"D:/网易云热歌/{music_name}.mp3", 'wb') as f:
        f.write(music_data)
 
 
if __name__ == '__main__':
    for music_id, music_name in zip_data:
        print('开始下载歌曲{}...'.format(music_name))
        # 网易云外播链接
        music_url = "http://music.163.com/song/media/outer/url?id=" + music_id
        # print(music_url)
        craw(music_url, music_name)
        print('歌曲{}下载完成'.format(music_name))

lxml及beautifulsoup

lxml 是一个 Python 库,用于处理 XML 和 HTML 文档。它提供了与标准库 xml.etree.ElementTree 类似的 API,但性能更好。lxml 还提供了额外的功能,例如支持 XPath 和 CSS 选择器。

lxml 解析 XML

<?xml version="1.0"?>
<root>
    <element id="1">
        <child>Text 1</child>
    </element>
    <element id="2">
        <child>Text 2</child>
    </element>
</root>
# 不知道为什么,安装在D盘的PyCharm我得添加安装在C盘的lxml路径才能使用
import sys
sys.path.append("C:\users/henry/appdata/roaming/python/python37/site-packages")
from lxml import etree

# 从文件读取 XML 文档
with open("sample.xml", "r") as file:
    xml_content = file.read()

# 解析 XML 文档
root = etree.fromstring(xml_content)

# 遍历所有 element 节点
for element in root.findall("element"):
    element_id = element.get("id")
    child_text = element.find("child").text
    print(f"Element ID: {element_id}, Child Text: {child_text}")
# Element ID: 1, Child Text: Text 1
# Element ID: 2, Child Text: Text 2

lxml 支持 XPath 表达式,这使得在 XML 或 HTML 文档中查找元素变得非常方便

from lxml import etree

# 从文件读取 XML 文档
with open("sample.xml", "r") as file:
    xml_content = file.read()

# 解析 XML 文档
root = etree.fromstring(xml_content)

# 使用 XPath 查找所有 element 节点
elements = root.xpath("//element")

for element in elements:
    element_id = element.get("id")
    child_text = element.xpath("./child/text()")[0]
    print(f"Element ID: {element_id}, Child Text: {child_text}")

# Element ID: 1, Child Text: Text 1
# Element ID: 2, Child Text: Text 2

lxml 解析 HTML

<!DOCTYPE html>
<html>
<head>
    <title>Sample HTML</title>
</head>
<body>
    <h1>Welcome to the website!</h1>
    <p>Here are some useful links:</p>
    <ul>
        <li><a href="https://www.example1.com">Example 1</a></li>
        <li><a href="https://www.example2.com">Example 2</a></li>
        <li><a href="https://www.example3.com">Example 3</a></li>
    </ul>
</body>
</html>
import sys
sys.path.append("C:\users/henry/appdata/roaming/python/python37/site-packages")
from lxml import html
# 从文件读取 HTML 文档
with open("sample.html", "r") as file:
    html_content = file.read()

# 解析 HTML 文档
root = html.fromstring(html_content)

# 提取所有链接
links = root.xpath("//a/@href")

for link in links:
    print(link)
# https://www.example1.com
# https://www.example2.com
# https://www.example3.com

 子网划分

和正则无关,仅仅是心血来潮

这个我分别用chatGPT3.5和4,都难以获得能够正确运行的代码

越是描述问题,它就越乱来,越改越乱。

之后发现要尽量引用出问题的代码或者变量

但是反而3.5的代码我改改能用,但是会有输出多个子网号相同的子网的问题。

将程序发给GPT4,并描述问题,最好一次只改一个问题,因为结果还是有子网号全0的情况,由于我一天只有一次机会,所以就不改了。

import math
import ipaddress


def subnet_calculator(class_type, subnetnum, host_counts):
    if class_type not in ['A', 'B', 'C']:
        raise ValueError("只能在标准网ABC里选")

    base_prefix = {'A': 8, 'B': 16, 'C': 24}

    subnets = []

    for i, host_count in enumerate(host_counts):
        # 需要的主机位数
        needed_bits = math.ceil(math.log2(host_count + 2))
        # 网络位
        subnet_mask = 8 - needed_bits
        # 前缀
        network_prefix = base_prefix[class_type] + subnet_mask

        # 生成子网地址
        subnetnum_int = int(ipaddress.IPv4Address(subnetnum))
        subnetnum_int += (1 << (32 - network_prefix)) * i
        subnet_address = ipaddress.IPv4Address(subnetnum_int)

        # 检查子网地址是否重复或全0/全1
        while any(subnet['subnet_address'].split('/')[0] == str(subnet_address) for subnet in subnets) or 
            subnet_address.is_unspecified or subnet_address.is_reserved:
            subnetnum_int += (1 << (32 - network_prefix))
            subnet_address = ipaddress.IPv4Address(subnetnum_int)

        subnet = {
            'subnet_address': f'{subnet_address}/{network_prefix}',
            'subnet_mask': str(ipaddress.IPv4Network((subnet_address, network_prefix), strict=False).netmask),
            'host_range': (str(ipaddress.IPv4Network((subnet_address, network_prefix), strict=False)[1]),
                           str(ipaddress.IPv4Network((subnet_address, network_prefix), strict=False)[-2]))
        }

        subnets.append(subnet)

    return subnets
# 示例输入
class_type = 'C'
subnetnum = '192.168.1.0'
host_counts = [20,6]
# 计算子网信息
subnet_info = subnet_calculator(class_type, subnetnum, host_counts)
# 打印结果
for i, subnet in enumerate(subnet_info):
    print(f"子网 {i + 1}:")
    print(f"子网地址: {subnet['subnet_address']}")
    print(f"子网掩码: {subnet['subnet_mask']}")
    print(f"主机范围(包括网关): {subnet['host_range'][0]} - {subnet['host_range'][1]}")
    print()

# 结果;
# 子网 1:
# 子网地址: 192.168.1.0/27
# 子网掩码: 255.255.255.224
# 主机范围(包括网关): 192.168.1.1 - 192.168.1.30

# 子网 2:
# 子网地址: 192.168.1.8/29
# 子网掩码: 255.255.255.248
# 主机范围(包括网关): 192.168.1.9 - 192.168.1.14

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。