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

正则表达式

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

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

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

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

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

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

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

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

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

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

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

*?最小就是0

+?最小就是1

??最小就是0

取消贪婪模式就是尽可能取最小,会尽可能匹配最小的子串,但并不一定是最小的。

这时应该有人敏锐的察觉不对(´・_・`)???

下面是个人发现

import re
print(re.findall(r"a.+c","abbbbbcabc"))
# ['abbbbbcabc']


print(re.findall(r"a.+?c","abbbbbcabc"))  
# ['abbbbbc', 'abc']  

print(re.findall(r"a.+c","aaabbbccc"))
# ['aaabbbccc']

print(re.findall(r"a.+?c","aaabbbccc"))
# ['aaabbbc']
# 其实最小子串是abbbc

 所以给我cpu整烧了,估计是算法有缺陷,或者是完美符合要求的算法在非常长的字符串中效率和性能极其低下。(它满足aaabbbc的时候就自动开始从后面开始匹配了,我记得在上数据结构与算法的时候细致讲解过字符串匹配算法的,算法会记录已经匹配到一半的字符串格式,例如用参数记录ABCdeABC,或是AAA此类类似的格式,记不清了,但那是字符串匹配算法,这是正则匹配)

(ಥ﹏ಥ)

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

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

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

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

[\] 转义符  

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

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

\s 匹配任何空白字符,它相当于在字符集中使用[\t\n\r\f\v]

\S 匹配任何非空白字符,它相当于在字符集中使用[^\t\n\r\f\v]

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

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

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

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

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

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

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

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

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

\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

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'}


def downloadpic(url):
    if not os.path.exists('D:\图片'):
        os.mkdir(r'D:\图片')
    response = requests.get(url, headers=headers).text
    # print(response)
    pattern1 = re.compile(r'(https://.*\.jpg)')
    pattern2 = re.compile(r'(https://.*\.webp)')
    response_img1 = pattern1.findall(response)
    response_img2 = pattern2.findall(response)
    # print(response_img1)
    # print(response_img2)
    response_img1.extend(response_img2)
    count = 0
    for picurl in response_img1:
        image_content = requests.get(picurl).content
        # print(image_content)
        count += 1
        with open('D:\图片' + '\\' + str(count) + '号.jpg', 'wb') as fw:
            print('正在保存第' + str(count) + '张图片')
            fw.write(image_content)
# 实例
if __name__ == '__main__':
    downloadpic('https://movie.douban.com/celebrity/1045259/photos/')

lxml库

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路径才能使用
# 路径在cmd窗口使用 pip show 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
sys.path.append("C:\\users/henry/appdata/roaming/python/python37/site-packages")
# 从文件读取 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

 BeautifulSoup库

 也是一个可以从HTML或XML文件中提取数据的Python库。它能够通过你喜欢的转换器实现惯用的文档导航、查找、修改文档的方式。BeautifulSoup将复杂的HTML文档转化为一个复杂的树形结构,每个节点都是Python对象。

获取html标签数据

from bs4 import BeautifulSoup

test_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>'''
soup = BeautifulSoup(test_html, "html.parser")

print(soup)
# 全部对象,行首顶格

# 输出整个ul标签
print(soup.ul)
# <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>

# 获取标签内的内容
print(soup.ul.text)
# Example 1
# Example 2
# Example 3

# 获取一个字典
print(soup.ul.li.a.attrs)
# {'href': 'https://www.example1.com'}

遍历结点 

遍历子节点:contents        children

遍历父节点:parent        parents        

遍历兄弟节点:next_sibling         previous_sibling        next_siblings         previous_siblings

next_siblingnext_siblings都是Beautiful Soup中用于遍历兄弟节点的方法,但是它们的返回值不同。

next_sibling方法返回当前节点的下一个兄弟节点,如果下一个兄弟节点不是标签,会继续往下查找,直到找到为止。如果找不到下一个兄弟节点,返回None

next_siblings方法返回当前节点后面的所有兄弟节点,以生成器的形式返回。如果当前节点没有后面的兄弟节点,返回一个空的生成器。

承接上面代码

# 遍历body元素的子节点
a=soup.body
print(a.contents)    
# ['\n', <h1>Welcome to the website!</h1>, '\n', <p>Here are some useful links:</p>, '\n', # <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>, '\n']
print(a.children)
# <list_iterator object at 0x00000259A68CB1C8>
# 生成一个列表迭代器对象,要用for循环遍历

# 遍历a元素的父节点
print(soup.find_all('a'))
#[<a href="https://www.example1.com">Example 1</a>, <a href="https://www.example2.com">Example 2</a>, <a href="https://www.example3.com">Example 3</a>]
for i in soup.find_all('a'):
    print(i.parent)
# <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>

# 遍历第二个li元素(下标为1)后面的所有兄弟节点
li = soup.ul.find_all('li')[1].next_siblings
for i in li:
    print(i)
# <li><a href="https://www.example3.com">Example 3</a></li>

# 遍历第二个li元素(下标为1)前面的所有兄弟节点
li = soup.ul.find_all('li')[1].previous_siblings
for i in li:
    print(i)
# <li><a href="https://www.example1.com">Example 1</a></li>

爬取图片函数(BeautifulSoup)

在实际爬虫应用中,还是通常使用requests库获取网页内容,然后传递给BeautifulSoup。

使用BeautifulSoup对象soup来查找所有包含图片链接的img标签。然后将这些标签的src属性添加到img_links列表中,用于下载图片。这样,我们就不再需要使用正则表达式来查找图片链接。

如果用soup = BeautifulSoup(response, 'html')语句解析HTML,也能执行相关操作但是,会有警告。

说是在另一个系统或不同的虚拟环境中运行这段代码,它可能会使用不同的解析器,从而导致不同的行为。可以在BeautifulSoup构造函数中传入额外的参数'features="lxml"',明确指定使用lxml解析器。

import os
import requests
from bs4 import BeautifulSoup

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'}

def downloadpic(url):
    if not os.path.exists('D:\图片'):
        os.mkdir(r'D:\图片')
    response = requests.get(url, headers=headers).text
    soup = BeautifulSoup(response, 'lxml')

    # 查找所有图片链接
    img_tags = soup.find_all('img', {'src': True})
    img_links = [img['src'] for img in img_tags]

    count = 0
    for picurl in img_links:
        image_content = requests.get(picurl).content
        count += 1
        with open('D:\图片' + '\\' + str(count) + '号.jpg', 'wb') as fw:
            print('正在保存第' + str(count) + '张图片')
            fw.write(image_content)

if __name__ == '__main__':
    downloadpic('https://movie.douban.com/celebrity/1045259/photos/')

 子网划分

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

这个我分别用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

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值