最后
🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
输出结果
<_sre.SRE_Match object; span=(5, 12), match=‘-python’>
<_sre.SRE_Match object; span=(17, 24), match=‘-python’>
None
环视在对字符串插入某些字符很有效,你可以利用它来匹配位置,然后插入对应的字符,而不需要对原来的文本进行替换。
捕获分组
在正则表达式中,分组可以帮助我们提取出想要的特定信息。
指明分组很简单,只需要在想捕获的表达式中两端加上()就可以了。在python中,我们可以用re.search(reg, xx).groups()来获取到所有的分组。
默认的()中都指明了一个分组,分组序号为i,i从1开始,分别用re.search(reg, xx).group(i)来获取。
如果不想捕获分组可以使用(?:…)来指明。
具体例子如下:
import re
reg7 = r’hello,([a-zA-Z0-9]+)’
print(re.search(reg7, ‘hello,world’).groups())
print(re.search(reg7, ‘hello,world’).group(1))
print(re.search(reg7, ‘hello,python’).groups())
print(re.search(reg7, ‘hello,python’).group(1))
输出结果
(‘world’,)
world
(‘python’,)
python
贪婪匹配
贪婪匹配是指正则表达式尽可能匹配多的字符,也就是趋于最大长度匹配。
正则表达式默认是贪婪模式。
例子如下:
import re
reg5 = r’hello.*world’
print(re.search(reg5, ‘hello world,hello python,hello world,hello javascript’))
输出结果
<_sre.SRE_Match object; span=(0, 36), match=‘hello world,hello python,hello world’>
由上可以看到它匹配的是hello world,hello python,hello world而不是刚开始的hello world。那如果我们只是想匹配刚开始的hello world,这时候我们可以利用正则表达式的非贪婪模式。
非贪婪匹配正好与贪婪匹配相反,它是指尽可能匹配少的字符,只要匹配到了就结束。要使用贪婪模式,仅需要在量词后面加上一个问号(?)就可以。
还是刚刚那个例子:
import re
reg5 = r’hello.*world’
reg6 = r’hello.*?world’
print(re.search(reg5, ‘hello world,hello python,hello world,hello javascript’))
print(re.search(reg6, ‘hello world,hello python,hello world,hello javascript’))
输出结果
<_sre.SRE_Match object; span=(0, 36), match=‘hello world,hello python,hello world’>
<_sre.SRE_Match object; span=(0, 11), match=‘hello world’>
由上可以看到这是我们刚刚想要匹配的效果。
进入开发
有了上面的基础知识,我们就可以进入开发环节了。
我们想实现的最终效果
本次我们的最终目的是写一个简单的python爬虫,这个爬虫能够下载一个静态网页,并且在保持网页引用资源的相对路径下下载它的静态资源(如js/css/images)。测试网站为http://www.peersafe.cn/index.html,效果图如下:
开发流程
我们的总体思路是先获取到网页的内容,然后利用正则表达式来提取我们想要的资源链接,最后就是下载资源。
获取网页内容
我们选用python3自带的urllib.http来发出http请求,或者你可以采用第三方请求库requests。
获取内容的部分代码如下:
url = ‘http://www.peersafe.cn/index.html’
读取网页内容
webPage = urllib.request.urlopen(url)
data = webPage.read()
content = data.decode(‘UTF-8’)
print(‘> 网站内容抓取完毕,内容长度:’, len(content))
获取到内容之后,我们需要把它保存下来,也就是写到本地磁盘上。我们定义一个SAVE_PATH路径,代表专门放置爬虫下载的文件。
python-spider-downloads是我们要放置的目录
这里推荐使用os模块来获取当前的目录或者拼接路径
不推荐直接使用’F://xxx’ + '//python-spider-downloads’等方式
SAVE_PATH = os.path.join(os.path.abspath(‘.’), ‘python-spider-downloads’)
接下来就是为这个站点创建一个单独的文件夹了。这个站点文件夹的格式是xxxx-xx-xx-domain,比如2018-08-03-www.peersafe.cn。在此之前,我们需要写一个函数来提取出一个url链接的域名、相对路径、请求文件名和请求参数等等,这个在后续在根据资源文件的引用方式创建相对应的文件夹时也会用到。
比如输入http://www.peersafe.cn/index.html,那么将会输出:
{‘baseUrl’: ‘http://www.peersafe.cn’, ‘fullPath’: ‘http://www.peersafe.cn/’, ‘protocol’: ‘http://’, 'domain
': ‘www.peersafe.cn’, ‘path’: ‘/’, ‘fileName’: ‘index.html’, ‘ext’: ‘html’, ‘params’: ‘’}
部分代码如下:
REG_URL = r’^(https?😕/|//)?((?:[a-zA-Z0-9-]+.)+(?:[a-zA-Z0-9-:]+))((?😕[-_.a-zA-Z0-9]?))((?<=/)[-a-zA-Z0-9]+(?:.([a-zA-Z0-9]+))+)?((?:?[a-zA-Z0-9%&=]))$’
regUrl = re.compile(REG_URL)
…
‘’’
解析URL地址
‘’’
def parseUrl(url):
if not url:
return
res = regUrl.search(url)
在这里,我们把192.168.1.109:8080的形式也解析成域名domain,实际过程中www.baidu.com等才是域名,192.168.1.109只是IP地址
(‘http://’, ‘192.168.1.109:8080’, ‘/abc/images/111/’, ‘index.html’, ‘html’, ‘?a=1&b=2’)
if res is not None:
path = res.group(3)
fullPath = res.group(1) + res.group(2) + res.group(3)
if not path.endswith(‘/’):
path = path + ‘/’
fullPath = fullPath + ‘/’
return dict(
baseUrl=res.group(1) + res.group(2),
fullPath=fullPath,
protocol=res.group(1),
domain=res.group(2),
path=path,
fileName=res.group(4),
ext=res.group(5),
params=res.group(6)
)
‘’’
解析路径
eg:
basePath => F:\Programs\python\python-spider-downloads
resourcePath => /a/b/c/ or a/b/c
return => F:\Programs\python\python-spider-downloads\a\b\c
‘’’
def resolvePath(basePath, resourcePath):
解析资源路径
res = resourcePath.split(‘/’)
去掉空目录 /a/b/c/ => [a, b, c]
dirList = list(filter(lambda x: x, res))
目录不为空
if dirList:
拼接出绝对路径
resourcePath = reduce(lambda x, y: os.path.join(x, y), dirList)
dirStr = os.path.join(basePath, resourcePath)
else:
dirStr = basePath
return dirStr
上面的正则表达式REG_URL有点长,这个正则表达式能解析目前我遇到的各种url形式,如果有不能解析的,你可以自行补充,我测试过的url列表可以去我的github中查看。
首先一个最复杂的url链接(比如’http://192.168.1.109:8080/abc/images/111/index.html?a=1&b=2’)来说,我们想分别提取出http://, 192.168.1.109:8080, /abc/images/111/, index.html, ?a=1&b=2。提取出/abc/images/111/的目的是为以后创建目录做准备,index.html是写入网页内容的名字。
有需要的可以深入研究一下REG_URL的写法,如果有更好的或者看不懂的,我们可以一起探讨。
有了parseUrl函数之后,我们就可以把刚刚获取网页内容和写入文件联系起来了,代码如下:
首先创建这个站点的文件夹
urlDict = parseUrl(url)
print(‘分析的域名:’, urlDict)
domain = urlDict[‘domain’]
filePath = time.strftime(‘%Y-%m-%d’, time.localtime()) + ‘-’ + domain
如果是192.168.1.1:8000等形式,变成192.168.1.1-8000,:不可以出现在文件名中
filePath = re.sub(r’:', ‘-’, filePath)
SAVE_PATH = os.path.join(SAVE_PATH, filePath)
读取网页内容
webPage = urllib.request.urlopen(url)
data = webPage.read()
content = data.decode(‘UTF-8’)
print(‘> 网站内容抓取完毕,内容长度:’, len(content))
把网站的内容写下来
pageName = ‘’
if urlDict[‘fileName’] is None:
pageName = ‘index.html’
else:
pageName = urlDict[‘fileName’]
pageIndexDir = resolvePath(SAVE_PATH, urlDict[‘path’])
if not os.path.exists(pageIndexDir):
os.makedirs(pageIndexDir)
pageIndexPath = os.path.join(pageIndexDir, pageName)
print(‘主页的地址:’, pageIndexPath)
f = open(pageIndexPath, ‘wb’)
f.write(data)
f.close()
提取有用的资源链接
我们想要的资源是图片资源,js文件、css文件和字体文件。如果我们要对网页内容一一进行解析,利用分组,来捕获出我们想要的链接形式,比如images/1.png和scripts/lib/jquery.min.js。
代码如下:
REG_RESOURCE_TYPE = r’(?:href|src|data-original|data-src)="'[a-zA-Z0-9?=.]*["‘]’
re.S代表开启多行匹配模式
regResouce = re.compile(REG_RESOURCE_TYPE, re.S)
…
解析网页内容,获取有效的链接
content是上一步读取到的网页内容
contentList = re.split(r’\s+', content)
resourceList = []
for line in contentList:
resList = regResouce.findall(line)
if resList is not None:
resourceList = resourceList + resList
下载资源
在解析出资源链接后,我们要针对每一个资源链接进行检查,把它变成符合http请求的url格式,比如把images/1.png加上http头和刚刚的domain,也就是http://domain/images/1.png。
下面是对资源链接进行处理的代码:
./static/js/index.js
/static/js/index.js
static/js/index.js
//abc.cc/static/js
http://www.baidu/com/static/index.js
if resourceUrl.startswith(‘./’):
resourceUrl = urlDict[‘fullPath’] + resourceUrl[1:]
elif resourceUrl.startswith(‘//’):
resourceUrl = ‘https:’ + resourceUrl
elif resourceUrl.startswith(‘/’):
resourceUrl = urlDict[‘baseUrl’] + resourceUrl
elif resourceUrl.startswith(‘http’) or resourceUrl.startswith(‘https’):
不处理,这是我们想要的url格式
pass
elif not (resourceUrl.startswith(‘http’) or resourceUrl.startswith(‘https’)):
static/js/index.js这种情况
resourceUrl = urlDict[‘fullPath’] + resourceUrl
else:
print(‘> 未知resource url: %s’ % resourceUrl)
接着就是对每个规范的资源链接进行解析(parseUrl),提取出它要存放的目录和文件名等等,然后创建对应的目录。
在这里,我也处理了引用的其他网站的资源。
解析文件,查看文件路径
resourceUrlDict = parseUrl(resourceUrl)
if resourceUrlDict is None:
print(‘> 解析文件出错:%s’ % resourceUrl)
continue
resourceDomain = resourceUrlDict[‘domain’]
resourcePath = resourceUrlDict[‘path’]
resourceName = resourceUrlDict[‘fileName’]
if resourceDomain != domain:
print(‘> 该资源不是本网站的,也下载:’, resourceDomain)
如果下载的话,根目录就要变了
再创建一个目录,用于保存其他地方的资源
resourceDomain = re.sub(r’:', ‘-’, resourceDomain)
savePath = os.path.join(SAVE_PATH, resourceDomain)
if not os.path.exists(SAVE_PATH):
print(‘> 目标目录不存在,创建:’, savePath)
os.makedirs(savePath)
continue
else:
savePath = SAVE_PATH
解析资源路径
dirStr = resolvePath(savePath, resourcePath)
if not os.path.exists(dirStr):
print(‘> 目标目录不存在,创建:’, dirStr)
os.makedirs(dirStr)
写入文件
downloadFile(resourceUrl, os.path.join(dirStr, resourceName))
下载的函数downloadFile的代码是:
‘’’
下载文件
‘’’
def downloadFile(srcPath, distPath):
global downloadedList
做了那么多年开发,自学了很多门编程语言,我很明白学习资源对于学一门新语言的重要性,这些年也收藏了不少的Python干货,对我来说这些东西确实已经用不到了,但对于准备自学Python的人来说,或许它就是一个宝藏,可以给你省去很多的时间和精力。
别在网上瞎学了,我最近也做了一些资源的更新,只要你是我的粉丝,这期福利你都可拿走。
我先来介绍一下这些东西怎么用,文末抱走。
(1)Python所有方向的学习路线(新版)
这是我花了几天的时间去把Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
最近我才对这些路线做了一下新的更新,知识体系更全面了。
(2)Python学习视频
包含了Python入门、爬虫、数据分析和web开发的学习视频,总共100多个,虽然没有那么全面,但是对于入门来说是没问题的,学完这些之后,你可以按照我上面的学习路线去网上找其他的知识资源进行进阶。
(3)100多个练手项目
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了,只是里面的项目比较多,水平也是参差不齐,大家可以挑自己能做的项目去练练。
(4)200多本电子书
这些年我也收藏了很多电子书,大概200多本,有时候带实体书不方便的话,我就会去打开电子书看看,书籍可不一定比视频教程差,尤其是权威的技术书籍。
基本上主流的和经典的都有,这里我就不放图了,版权问题,个人看看是没有问题的。
(5)Python知识点汇总
知识点汇总有点像学习路线,但与学习路线不同的点就在于,知识点汇总更为细致,里面包含了对具体知识点的简单说明,而我们的学习路线则更为抽象和简单,只是为了方便大家只是某个领域你应该学习哪些技术栈。
(6)其他资料
还有其他的一些东西,比如说我自己出的Python入门图文类教程,没有电脑的时候用手机也可以学习知识,学会了理论之后再去敲代码实践验证,还有Python中文版的库资料、MySQL和HTML标签大全等等,这些都是可以送给粉丝们的东西。
这些都不是什么非常值钱的东西,但对于没有资源或者资源不是很好的学习者来说确实很不错,你要是用得到的话都可以直接抱走,关注过我的人都知道,这些都是可以拿到的。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!