题目
使用python爬取网页url,并对此数据进行权重及访问概率分析。
思路
首先打开网页分析网页源代码,我爬取的目标是新浪微博官网https://www.sina.com.cn/。需要的URL均在网页源码中,因此先爬取网页源码,在网页源码中挑出我们需要的所有URL。同时先获取父类链接,通过父类进入子类爬取子类链接。我们要分析的是网页中所有URL的权重以及访问概率,所以网页中的script链接是我们不需要的,例如:。应该在获取完所有URL后排除。进而获取到网页源码中所需要的所有链接,并保存在sina_url.txt中。
面临的问题
- 没有设置广度优先爬取的深度,导致过多网址以及不是本网站的网址。
- 不能很好的保存下网址之间的关系。
- 使用多进程爬取速度很快,在没有设置爬取深度时,会持续爬取,导致爬取数据太多给服务器造成压力引起不必要的麻烦。
解决方法
- 设置爬取深度为100,广度不限,大概率避免掉不属于本网站的网址。
- 运用多进程爬取,爬取队列往往先爬取父网页链接,之后才能得到子网页链接。根据存储先后得到网址之间的关系,并保存。
- 为避免多进程爬取过多数据引起不必要的麻烦,两种解决方式:1.设置爬取深度2.短时间运行程序,手动终止,分析所获取到的部分数据即可(运行10秒大概1762个URL)。
分析权重
使用PageRank算法,对获取到的URL进行分析。
PageRank算法总的来说就是预先给每个网页一个PR值(下面用PR值指代PageRank值),由于PR值物理意义上为一个网页被访问概率,所以一般是1/N,其中N为网页总数。另外,一般情况下,所有网页的PR值的总和为1。如果不为1的话也不是不行,最后算出来的不同网页之间PR值的大小关系仍然是正确的,只是不能直接地反映概率了。预先给定PR值后,通过下面的算法不断迭代,直至达到平稳分布为止。
公式为:
P R ( x ) = a ∑ Y i ϵ S ( x ) P R ( Y i ) n i + 1 − a N PR(x)=a\sum_{Y_i \epsilon\ S(x)}\frac {PR(Y_i)}{n_i}+\frac {1-a}{N} PR(x)=aYiϵ S(x)∑niPR(Yi)+N1−a
其中S(X)表示,指向网页X的所有网页的集合,n_i表示网页Y_i的出边数量,N表示所有网页总数,α一般取0.85。
定义mypagerank()函数参数为edges,获取边节点集合,先将链中没有出链的节点改为对所有节点都有出链,给每个节点赋予初始的PR值,遍历所有“入射”的页面。不断迭代,最后根据结果分析出各URL权重,进而得到被访问概率。
绘图
根据爬虫爬取到的数据及PageRank所算得的结果,利用函数库进行制图。
相关代码
spiderAdmin.py #爬虫代码
# @File : spiderTest.py
# @Software: PyCharm
from urllib import request
from urllib import error
from bs4 import BeautifulSoup
import re
from multiprocessing import Pool
from multiprocessing import Manager
import time
def getHtml(url, ua_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko',
num_retries=5):
headers = {"User-Agent": ua_agent}
req = request.Request(url, headers=headers)
html = None
try:
response = request.urlopen(req)
html = response.read().decode('utf-8')
except error.URLError or error.HTTPError as e:
if num_retries > 0:
if hasattr(e, 'code') and 500 <= e.code < 600:
getHtml(url, ua_agent, num_retries - 1)
return html
def get_urls(html):
"""
获取当前页面中所有的超链接的列表信息
"""
links = []
soup = BeautifulSoup(html, "html.parser")
url_list = soup.find_all('a')
for link in url_list:
links.append(link.get('href'))
return links
# 匹配规则^http或者com$,cn$
def save_file(murl, fileName):
with open(fileName, 'ab') as f:
f.write(murl.encode())
def CrawlInfo(url, q):
# 获取当前节点的信息
global crawl_queue
crawl_queue = [] # 声明待爬队列
hlinks = []
html = getHtml(url)
links = get_urls(html)
for murl in links:
if re.findall("^http", str(murl)):
murl = str(murl) + "\r\n"
hlinks.append(murl)
save_file(murl, "sina_url.txt")
elif re.findall("^java", str(murl)):#去除带有javascript标签的url
links.remove(murl)
elif re.findall("gsp$",str(murl)) or re.findall("shtml$", str(murl)) or re.findall("[0-9]*$", str(murl)):
murl = "https://www.sina.com" + str(murl) + "\r\n"
hlinks.append(murl)
save_file(murl, "sina_url.txt")
elif re.findall("<a href=\"(.*?)\" target=\"_blank\"><b>.*?</b></a>", str(murl)):
murl = str(murl)
hlinks.append(murl)
save_file(murl, "sina_url.txt")
else:
pass
for _ in hlinks:
crawl_queue.append(_)
time.sleep(0.001)
q.put(url)
if __name__ == "__main__":
# 使用进程池
pool = Pool()
q = Manager().Queue()
crawled_queue = [] # 已爬队列
seedUrl = "https://www.sina.com.cn/"
CrawlInfo(seedUrl, q)
crawl_queue.append(seedUrl)
crawl_queue = list(set(crawl_queue))
while crawl_queue:
url = crawl_queue.pop(0)
pool.apply_async(func=CrawlInfo, args=(url, q))
url = q.get()
crawled_queue.append(url)
pool.close()
pool.join()
爬取网页URL时,手动终止程序,爬取结果如下。
接着选取前11条URL进行分析,并用大写A-K分别代表这些URL
将这些URL的关系按照A B:即从A可跳转到B,输入保存到url.txt中
url.txt:
A B
A D
A G
A J
A K
B A
C A
D A
E A
F A
G A
H A
I A
J A
K A
B C
B F
J I
G H
D E
spiderTest.py #分析代码
# @File : spiderTest.py
# @Software: PyCharm
import math
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import pandas as pd
from bokeh.plotting import figure, output_file, show
# noinspection PyUnresolvedReferences
from bokeh.models import Plot, Range1d, MultiLine, Circle, HoverTool, BoxZoomTool, ResetTool, ColorBar, LogColorMapper
from bokeh.models.graphs import from_networkx
from bokeh.transform import cumsum
from bokeh.palettes import Category20c, Spectral4
def plot_graph(edges):
G = nx.DiGraph()
for edge in edges:
G.add_edge(edge[0], edge[1])
nx.draw(G, with_labels=True)
plt.show()
def mypagerank(edges):
nodes = []
for edge in edges:
if edge[0] not in nodes:
nodes.append(edge[0])
if edge[1] not in nodes:
nodes.append(edge[1])
print(nodes)
N = len(nodes)
i = 0
node_to_num = {}
for node in nodes:
node_to_num[node] = i
i += 1
for edge in edges:
edge[0] = node_to_num[edge[0]]
edge[1] = node_to_num[edge[1]]
print(edges)
S = np.zeros([N, N])
for edge in edges:
S[edge[1], edge[0]] = 1
print(S)
for j in range(N):
sum_of_col = sum(S[:, j])
for i in range(N):
if sum_of_col != 0:
S[i, j] /= sum_of_col
else:
S[i, j] = 1 / N
print(S)
alpha = 0.85
A = alpha * S + (1 - alpha) / N * np.ones([N, N])
print(A)
# 生成初始的PageRank值,记录在P_n中,P_n和P_n1均用于迭代
P_n = np.ones(N) / N
P_n1 = np.zeros(N)
e = 100000 # 误差初始化
k = 0 # 记录迭代次数
print('loop...')
while e > 0.00000001: # 开始迭代
P_n1 = np.dot(A, P_n) # 迭代公式
e = P_n1 - P_n
e = max(map(abs, e)) # 计算误差
P_n = P_n1
k += 1
print('iteration %s:' % str(k), P_n1)
print('final result:', P_n)
return P_n
def plot_values(df):
df.plot(kind='bar',colormap='gist_rainbow',title="Probability calculation results")
plt.xticks(range(len(nodes)),nodes)
plt.show()
def bokeh_plot(df):
xdata = df['节点']
ydata = df['访问概率']
p = figure(x_range=xdata, plot_height=350,title="bokeh可视化结果",x_axis_label="网址",y_axis_label='访问概率')
p.vbar(x=xdata, top=ydata, width=0.9)
p.line(range(len(xdata)),ydata, legend_label="Temp.", line_width=2)
output_file('./各网址访问概率.html')
show(p)
def bokeh_plot2(edges):
G = nx.DiGraph()
for edge in edges:
G.add_edge(edge[0], edge[1])
SAME_CLUB_COLOR, DIFFERENT_CLUB_COLOR = "black", "red"
edge_attrs = {}
for start_node, end_node, _ in G.edges(data=True):
edge_color = SAME_CLUB_COLOR if G.nodes[start_node]== G.nodes[end_node] else DIFFERENT_CLUB_COLOR
edge_attrs[(start_node, end_node)] = edge_color
nx.set_edge_attributes(G, edge_attrs, "edge_color")
# Show with Bokeh
plot = Plot(plot_width=400, plot_height=400,
x_range=Range1d(-1.1, 1.1), y_range=Range1d(-1.1, 1.1))
plot.title.text = "Graph Interaction Demonstration"
node_hover_tool = HoverTool(tooltips=[("index", "@index"), ("club", "@club")])
plot.add_tools(node_hover_tool, BoxZoomTool(), ResetTool())
graph_renderer = from_networkx(G, nx.spring_layout, scale=1, center=(0, 0))
graph_renderer.node_renderer.glyph = Circle(size=15, fill_color=Spectral4[0])
graph_renderer.edge_renderer.glyph = MultiLine(line_color="edge_color", line_alpha=0.8, line_width=1)
plot.renderers.append(graph_renderer)
output_file("网络关系图.html")
show(plot)
def bokeh_plot3(nodes,res):
output_file("pie.html")
x=dict(zip(nodes,res))
data = pd.Series(x).reset_index(name='value').rename(columns={'index': 'country'})
data['angle'] = data['value'] / data['value'].sum() * 2 * math.pi
data['color'] = Category20c[len(x)]
p = figure(plot_height=350, title="Pie Chart",tooltips="@country: @value", x_range=(-0.5, 1.0))
p.wedge(x=0, y=1, radius=0.4,
start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
line_color="white", fill_color='color', legend_field='country', source=data)
show(p)
if __name__ == '__main__':
# 读入有向图,存储边
f = open('url.txt', 'r')
edges = [line.strip('\n').split(' ') for line in f]
print(edges)
plot_graph(edges)
res= mypagerank(edges)
nodes=['A','B','C','D','E','F','G','H','I','J','K']
data={"节点":nodes,"访问概率":res}
df = pd.DataFrame(data)
plot_values(df) #直方图 重要程度
bokeh_plot(df) #直方图 访问概率
bokeh_plot2(edges) #网络关系
bokeh_plot3(nodes,res) #饼状图
绘图结果
总结
爬虫技术在当今可以用来进行网络数据采集、大数据分析、网页分析等等。简单说就是利用爬虫技术采集互联网中的信息,采集回来之后进行相对应的储存以及处理的过程。
因为这些功能,爬虫技术在互联网上起到了关键性的作用,爬虫让我们的生活多元化,爬虫就像一个探测器,他模拟人的行为去各个网站溜达,点点按钮,查查数据,然后把看到了的数据背回来。就像一个蜘蛛在网络中不停的爬来爬去,极大的方便了人们的生活。
但是,有利就有弊。爬虫因为其强大的能力一但滥用就会给网络带来很多麻烦。例如,多线程的控制,如果不加节制,使其以极快的速度不停的访问网站,很多小网站的服务器都承受不住这么大的压力,导致服务器宕机。侵害了别人的利益。爬虫技术更像是一把双刃剑,技术本身是无罪的主要看使用爬虫技术的人如何运用。当然爬取网络上的公开信息还是不算违法的,如果你想利用爬虫技术窃取隐私信息来牟利则是万万不可取的。