2021-07-23 Python爬虫并分析网页URL

题目

使用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)+N1a

其中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)  #饼状图

绘图结果

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

总结

爬虫技术在当今可以用来进行网络数据采集、大数据分析、网页分析等等。简单说就是利用爬虫技术采集互联网中的信息,采集回来之后进行相对应的储存以及处理的过程。
因为这些功能,爬虫技术在互联网上起到了关键性的作用,爬虫让我们的生活多元化,爬虫就像一个探测器,他模拟人的行为去各个网站溜达,点点按钮,查查数据,然后把看到了的数据背回来。就像一个蜘蛛在网络中不停的爬来爬去,极大的方便了人们的生活。
但是,有利就有弊。爬虫因为其强大的能力一但滥用就会给网络带来很多麻烦。例如,多线程的控制,如果不加节制,使其以极快的速度不停的访问网站,很多小网站的服务器都承受不住这么大的压力,导致服务器宕机。侵害了别人的利益。爬虫技术更像是一把双刃剑,技术本身是无罪的主要看使用爬虫技术的人如何运用。当然爬取网络上的公开信息还是不算违法的,如果你想利用爬虫技术窃取隐私信息来牟利则是万万不可取的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值