用正则表达式提取网站所有的http链接
爬虫的目标网站是 https://www.lz13.cn/ , 我想要将这个网站里的每一篇文章都抓取下来,就必须获得网站内部的每一个页面的url,具体思路如下:
进入网站首页,使用正则表达式提取所有以https://www.lz13.cn/ 开头的url
逐个爬取第一步中所提取到的url,并将新网页里的符合要求的url也都提取出来
如果你有数据结构的功底,一定已经看明白这其中的门道了。从一个url进入到一个页面,这个页面里还有会其他的http连接,进入这其中某个连接后,又可以得到新的http链接,他们组成了一颗树。我的程序可以选择广度优先遍历,也可以选择深度优先遍历,但不论哪种遍历方法,我都需要将一个页面里的所有http链接都提取出来。
已经访问过的url,可以放到一个集合中,访问新的url前,先检查这个url是否已经被访问过了,对于单个网站,url数量不会太多,撑死了几万个,使用集合就可以保存他们。
思路已经有了,先来实现第一步,提取一个页面里的所有http连接
首先实现一个函数,来获得指定url的html源码
import requests
def get_html(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36',
}
res = requests.get(url, headers=headers)
return res.text
这个函数我实现的非常简单,并没有考虑请求超时和http代理,我们先快速的实现功能,遇到问题再去解决问题。
def extract_all_urls(html):
pattren = re.compile(r'https://www.lz13.cn/[^\s]+.html')
url_lst = pattren.findall(html)
return url_lst
正则表达式匹配以https://www.lz13.cn/开头,以.html结尾的文本,这样设计表达式基于两个考虑
只提取本站内的链接,其他诸如友情链接不考虑
只提取html页面,其他如js,css 不需要提取
def get_urls_from_url(url):
html = get_html(url)
url_lst = extract_all_urls(html)
return url_lst
首先实现get_urls_from_url,它负责对一个url发出请求,调用extract_all_urls获得这个url里的全部链接。
接下来实现提取全站url的函数
import requests
import re
import time
from collections import deque
def get_html(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36',
}
res = requests.get(url, headers=headers)
return res.text
def extract_all_urls(html):
pattren = re.compile(r'https://www.lz13.cn/[^\s]+.html')
url_lst = pattren.findall(html)
return url_lst
def get_urls_from_url(url):
html = get_html(url)
url_lst = extract_all_urls(html)
return url_lst
def get_all_urls(web_site):
url_access_set = set() # 已经访问过的url
queue_url_set = set()
url_lst = get_urls_from_url(web_site)
url_access_set.add(web_site)
queue = deque()
for url in url_lst:
queue.append(url)
queue_url_set.add(url)
while len(queue) != 0:
print(len(queue))
url = queue.popleft()
if url in url_access_set:
continue
url_access_set.add(url)
url_lst = get_urls_from_url(url)
for url in url_lst:
if url not in queue_url_set:
queue.append(url)
queue_url_set.add(url)
return url_access_set
if __name__ == '__main__':
all_urls = get_all_urls('https://www.lz13.cn')
print(len(all_urls))
get_all_urls函数实现的是广度优先遍历,原以为这个网站没多少url呢,结果跑了一阵子,queue里的url越来越多,看来单线程跑是不现实的,这样子不知道要跑到什么时候才能把整个网站的url都遍历一遍,在后面的文章里,我会改造这份程序,加快爬取的速度。