【爬虫之路】一点有关学习BeautifulSoup的笔记

本文介绍了如何使用BeautifulSoup库在Python2.7环境下进行网页HTML解析,重点讨论了解决中文乱码问题、通过tag和id值查找内容的方法,并提到了其灵活的查找特性。作为一个简单的练习,作者爬取了队友的博客内容。
摘要由CSDN通过智能技术生成

不务正业也要按照基本法。。

资料参考:http://cuiqingcai.com/1319.html.

BeautifulSoup是python的一个html解析库,最新版本是bs4,但不兼容python3,所以这次是用的python2.7写法。。

导入主要用到的三个库:

import requests
from bs4 import BeautifulSoup
import re

首先要解决一下乱码问题:
由于ubuntu下的python默认解码是ascii,所以一有中文就爆炸,会出现这样的情况:

'ascii' codec can't encode characters in position 848-851: ordinal not in range(128)

于是在代码里加上这两句修改一下,当然也有其他办法,不予赘述了(天灭python2,选3保平安,网搜“python2 中文乱码”有真相!)

import sys
reload(sys)
sys.setdefaultencoding('utf8')

接下来,先爬取一个网页的html源码,这里以队友博客的一篇 题解 为例,html源码太长,可以自行查看。

然后建立一个BeautifulSoup对象:

soup = BeautifulSoup(html)
#print soup.prettify()

print soup.prettify()可以看到html源码以树状结构存储了起来。

尝试通过tag标签查找内容:

print soup.title

得到:

<title>HDU 4275 Color the Tree(哈希+树同构+组合数学+树形dp) - GODSPEED
        - 博客频道 - CSDN.NET</title>

此外还有很多属性:

print soup.title.string
#HDU 4275 Color the Tree(哈希+树同构+组合数学+树形dp) - GODSPEED
#        - 博客频道 - CSDN.NET
print soup.title.name
#title
...

值得注意的是,这里通过tag来查找,只返回匹配的第一个节点标签数据,如果要得到所有该标签数据,则需要遍历文档树来实现。这个结构有点像trie树,查找搜索方式很灵活,具体可参考开头提到的文档。

接下来尝试按照标签里的id值来搜索:

info = soup.select("#blog_rank")
print info

得到:

[<ul id="blog_rank">
<li>访问:<span>29690次</span></li>
<li>积分:<span>2605</span> </li>
<li>等级: <span style="position:relative;display:inline-block;z-index:1">
<img alt="" id="leveImg" src="http://c.csdnimg.cn/jifen/images/xunzhang/jianzhang/blog5.png" style="vertical-align: middle;"/>
<div id="smallTittle" style=" position: absolute;  left: -24px;  top: 25px;  text-align: center;  width: 101px;  height: 32px;  background-color: #fff;  line-height: 32px;  border: 2px #DDDDDD solid;  box-shadow: 0px 2px 2px rgba (0,0,0,0.1);  display: none;   z-index: 999;">
<div style="left: 42%;  top: -8px;  position: absolute;  width: 0;  height: 0;  border-left: 10px solid transparent;  border-right: 10px solid transparent;  border-bottom: 8px solid #EAEAEA;"></div>
            积分:2605 </div>
</span> </li>
<li>排名:<span>第7622名</span></li>
</ul>]

除此之外还有类名查找,组合查找等。。具体内容,还是参考开头提到的博客。。

写到这里其实就差不多了。。这次比较无聊地把队友题解内容和代码给爬取了下来,虽然没什么卵用,算做练一下bs和正则表达式的运用吧。

soup = BeautifulSoup(html)
code = soup.select("#article_content")
print code #未做处理的提取内容
code = str(code[0])
code = re.compile(r'<[^>]+>').sub('', code)#去掉标签
code = code.replace('&lt;','<')
code = code.replace('&gt;','>')
code = code.replace('&amp;','&') #替换html特殊字符,这里其实可以写成一个函数的。。
print code
[<div class="article_content" id="article_content">
<p><span style="font-family:SimHei; font-size:18px">题意:给出一颗n个结点的数,现在用m种颜色给每个节点染色,问不重复(通过旋转)的染色方法数有多少种。</span></p>
<p><span style="font-family:SimHei; font-size:18px">思路:这道题主要是要解决对称而导致的重复的问题,所以选择根节点时要选择树的中心,所以可以先bfs求出树的直径,如果直径上的结点个数为奇数,那么取直径的中点作为中心dfs,如果是偶数那么在两个中点位置的中间新建一个节点。</span></p>
<p><span style="font-family:SimHei; font-size:18px">用dp[i]表示以i为节点的子树中方案有多少种 ,那么对子树的形态哈希后排序,哈希值相同的子树相邻,如果某一个哈希值(即同构的子树)的子树有n个,这种子树形态的方案数为m,那么由组合数学可以得出这些子树组合起来的方案数为C(n+m-1, n),这个可以由挡板法求出,假设m-1个挡板和m个子树,从这些中选出m棵子树构成一种方案的方法有C(n+m-1, n)种。</span></p>
<p><span style="font-family:SimHei; font-size:18px">然后就是一个普通的树形dp了,因为我们是从中心开始dfs的,这样保证了不会遗漏所有同构的子树。</span></p>
<p><span style="font-family:SimHei; font-size:18px">ps:一开始哈希的素数又选搓了.....wa了一晚上加一早上........个人体会....选素数的时候不要选择太小的,最好这个素数的平方在mod的附近,这样可以保证哈希不具有太强的线性。</span></p>
<div style="top:0px">
<p></p><pre class="cpp" name="code">#include&lt;cstdio&gt;
#include&lt;cstring&gt;
#include&lt;cmath&gt;
#include&lt;cstdlib&gt;
#include&lt;iostream&gt;
#include&lt;algorithm&gt;
#include&lt;vector&gt;
#include&lt;map&gt;
#include&lt;queue&gt;
#include&lt;stack&gt;
#include&lt;string&gt;
#include&lt;map&gt;
#include&lt;set&gt;
#include&lt;ctime&gt;
#define eps 1e-6
#define LL long long
#define pii pair&lt;int, int&gt;
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 50500;
const int MOD = 1e9+7;
const int P = 10003;
const int A = 131237; 
int n, m;
vector&lt;int&gt; G[MAXN];
bool vis[MAXN];
int pre[MAXN], dis[MAXN];
LL inv[MAXN]; 
struct Node {
    LL hash, ans;
    bool operator &lt; (const Node&amp; A) const {
        return hash &lt; A.hash;
    }
};
LL pow_mod(LL a, LL p, LL n) {
    if(p == 0) return 1;
    LL ans = pow_mod(a, p/2, n);
    ans = ans * ans % n;
    if(p%2 == 1) ans = ans * a % n;
    return ans;
} 
void init() {
    for(int i = 1; i &lt;= 50005; i++) inv[i] = pow_mod(i, MOD-2, MOD);
}
LL C(int n, int m) {
    LL ans = 1;
    for(int i = n-m+1; i &lt;= n; i++) ans = ans * i % MOD;
    for(int i = 1; i &lt;= m; i++) ans = ans * inv[i] % MOD; 
    return ans;
}
int bfs1() {
    memset(vis, 0, sizeof(vis));
    queue&lt;int&gt; q;
    q.push(1);
    vis[1] = 1;
    int ans = 1;
    while(!q.empty()) {
        int t = q.front();
        q.pop();
        for(int i = 0; i &lt; G[t].size(); i++) {
            int u = G[t][i];
            if(vis[u]) continue;
            vis[u] = 1;
            q.push(u);
            ans = u;
        }
    }
    return ans;
} 
int bfs2(int cur) {
    memset(vis, 0, sizeof(vis));
    queue&lt;int&gt; q;
    q.push(cur);
    vis[cur] = 1;
    dis[cur] = 1;
    int ans = cur;
    while(!q.empty()) {
        int t = q.front();
        q.pop();
        for(int i = 0; i &lt; G[t].size(); i++) {
            int u = G[t][i];
            if(vis[u]) continue;
            vis[u] = 1;
            q.push(u);
            ans = u;
            pre[u] = t; 
            dis[u] = dis[t] + 1;
        }
    }
    if(dis[ans] &amp; 1) {
        int tmp = dis[ans] / 2; 
        while(tmp--) ans = pre[ans];
        return ans;
    }
    else {

        int pos, tmp = dis[ans]/2 - 1;
        while(tmp--) ans = pre[ans];
        pos = pre[ans];
        int root = n + 1;
        G[root].push_back(ans);
        G[root].push_back(pos);
        for(vector&lt;int&gt;::iterator it = G[ans].begin(); it != G[ans].end(); it++) 
            if(*it == pos) {
                G[ans].erase(it);
                break;
            }
        for(vector&lt;int&gt;::iterator it = G[pos].begin(); it != G[pos].end(); it++) 
            if(*it == ans) {
                G[pos].erase(it);
                break;
            }
        //cout &lt;&lt; ans &lt;&lt; " " &lt;&lt; pos &lt;&lt; endl;
        return root;
    }
} 

Node dfs(int cur, int fa) {
    vector&lt;Node&gt; val;
    for(int i = 0; i &lt; G[cur].size(); i++) {
        int u = G[cur][i];
        if(u == fa) continue;
        Node tmp = dfs(u, cur);
        val.push_back(tmp);
    }
    sort(val.begin(), val.end());
    Node ret;
    ret.hash = A, ret.ans = 1;
    int sz = val.size();
    for(int i = 0; i &lt; sz;) {
        int j = i;
        while(j&lt;sz &amp;&amp; val[j].hash==val[i].hash) {
            ret.hash *= P;
            ret.hash ^= val[i].hash;
            ret.hash %= MOD;
            j++;
        }
        ret.ans *= C(j-i+val[i].ans-1, j-i);
        ret.ans %= MOD;
        i = j;
        //cout &lt;&lt; val[i].ans &lt;&lt; " " &lt;&lt; cur &lt;&lt; " " &lt;&lt; i &lt;&lt; endl;
    }
    if(cur &lt;= n) ret.ans = ret.ans * m % MOD;
    //cout &lt;&lt; ret.ans &lt;&lt; endl;;
    return ret;
}
int main() {
    //freopen("input.txt", "r", stdin);
    init();
    while(scanf("%d%d", &amp;n, &amp;m) == 2) {
        for(int i = 1; i &lt;= n+1; i++) G[i].clear();
        for(int i = 1, u, v; i &lt; n; i++) {
            scanf("%d%d", &amp;u, &amp;v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        int tmp = bfs1();
        int root = bfs2(tmp);
        Node ans = dfs(root, 0);
        cout &lt;&lt; ans.ans &lt;&lt; endl;
    }
    return 0;
}
















</pre><br/>

</div>
<div style="padding-top:20px">
<p style="font-size:12px;">版权声明:本文为Godspeed原创文章,未经博主允许不得转载。</p>
</div>
</div>]
题意:给出一颗n个结点的数,现在用m种颜色给每个节点染色,问不重复(通过旋转)的染色方法数有多少种。
思路:这道题主要是要解决对称而导致的重复的问题,所以选择根节点时要选择树的中心,所以可以先bfs求出树的直径,如果直径上的结点个数为奇数,那么取直径的中点作为中心dfs,如果是偶数那么在两个中点位置的中间新建一个节点。
用dp[i]表示以i为节点的子树中方案有多少种 ,那么对子树的形态哈希后排序,哈希值相同的子树相邻,如果某一个哈希值(即同构的子树)的子树有n个,这种子树形态的方案数为m,那么由组合数学可以得出这些子树组合起来的方案数为C(n+m-1, n),这个可以由挡板法求出,假设m-1个挡板和m个子树,从这些中选出m棵子树构成一种方案的方法有C(n+m-1, n)种。
然后就是一个普通的树形dp了,因为我们是从中心开始dfs的,这样保证了不会遗漏所有同构的子树。
ps:一开始哈希的素数又选搓了.....wa了一晚上加一早上........个人体会....选素数的时候不要选择太小的,最好这个素数的平方在mod的附近,这样可以保证哈希不具有太强的线性。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 50500;
const int MOD = 1e9+7;
const int P = 10003;
const int A = 131237; 
int n, m;
vector<int> G[MAXN];
bool vis[MAXN];
int pre[MAXN], dis[MAXN];
LL inv[MAXN]; 
struct Node {
    LL hash, ans;
    bool operator < (const Node& A) const {
        return hash < A.hash;
    }
};
LL pow_mod(LL a, LL p, LL n) {
    if(p == 0) return 1;
    LL ans = pow_mod(a, p/2, n);
    ans = ans * ans % n;
    if(p%2 == 1) ans = ans * a % n;
    return ans;
} 
void init() {
    for(int i = 1; i <= 50005; i++) inv[i] = pow_mod(i, MOD-2, MOD);
}
LL C(int n, int m) {
    LL ans = 1;
    for(int i = n-m+1; i <= n; i++) ans = ans * i % MOD;
    for(int i = 1; i <= m; i++) ans = ans * inv[i] % MOD; 
    return ans;
}
int bfs1() {
    memset(vis, 0, sizeof(vis));
    queue<int> q;
    q.push(1);
    vis[1] = 1;
    int ans = 1;
    while(!q.empty()) {
        int t = q.front();
        q.pop();
        for(int i = 0; i < G[t].size(); i++) {
            int u = G[t][i];
            if(vis[u]) continue;
            vis[u] = 1;
            q.push(u);
            ans = u;
        }
    }
    return ans;
} 
int bfs2(int cur) {
    memset(vis, 0, sizeof(vis));
    queue<int> q;
    q.push(cur);
    vis[cur] = 1;
    dis[cur] = 1;
    int ans = cur;
    while(!q.empty()) {
        int t = q.front();
        q.pop();
        for(int i = 0; i < G[t].size(); i++) {
            int u = G[t][i];
            if(vis[u]) continue;
            vis[u] = 1;
            q.push(u);
            ans = u;
            pre[u] = t; 
            dis[u] = dis[t] + 1;
        }
    }
    if(dis[ans] & 1) {
        int tmp = dis[ans] / 2; 
        while(tmp--) ans = pre[ans];
        return ans;
    }
    else {

        int pos, tmp = dis[ans]/2 - 1;
        while(tmp--) ans = pre[ans];
        pos = pre[ans];
        int root = n + 1;
        G[root].push_back(ans);
        G[root].push_back(pos);
        for(vector<int>::iterator it = G[ans].begin(); it != G[ans].end(); it++) 
            if(*it == pos) {
                G[ans].erase(it);
                break;
            }
        for(vector<int>::iterator it = G[pos].begin(); it != G[pos].end(); it++) 
            if(*it == ans) {
                G[pos].erase(it);
                break;
            }
        //cout << ans << " " << pos << endl;
        return root;
    }
} 

Node dfs(int cur, int fa) {
    vector<Node> val;
    for(int i = 0; i < G[cur].size(); i++) {
        int u = G[cur][i];
        if(u == fa) continue;
        Node tmp = dfs(u, cur);
        val.push_back(tmp);
    }
    sort(val.begin(), val.end());
    Node ret;
    ret.hash = A, ret.ans = 1;
    int sz = val.size();
    for(int i = 0; i < sz;) {
        int j = i;
        while(j<sz && val[j].hash==val[i].hash) {
            ret.hash *= P;
            ret.hash ^= val[i].hash;
            ret.hash %= MOD;
            j++;
        }
        ret.ans *= C(j-i+val[i].ans-1, j-i);
        ret.ans %= MOD;
        i = j;
        //cout << val[i].ans << " " << cur << " " << i << endl;
    }
    if(cur <= n) ret.ans = ret.ans * m % MOD;
    //cout << ret.ans << endl;;
    return ret;
}
int main() {
    //freopen("input.txt", "r", stdin);
    init();
    while(scanf("%d%d", &n, &m) == 2) {
        for(int i = 1; i <= n+1; i++) G[i].clear();
        for(int i = 1, u, v; i < n; i++) {
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        int tmp = bfs1();
        int root = bfs2(tmp);
        Node ans = dfs(root, 0);
        cout << ans.ans << endl;
    }
    return 0;
}




















版权声明:本文为Godspeed原创文章,未经博主允许不得转载。

另一位队友的:(http://www.cnblogs.com/Cw-trip/p/4898112.html

import requests
from bs4 import BeautifulSoup
import re
import sys

reload(sys)
sys.setdefaultencoding('utf8')
url = 'http://www.cnblogs.com/Cw-trip/p/4898112.html'
headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:41.0) Gecko/20100101 Firefox/41.0'}
html = requests.get(url, headers=headers).content
soup = BeautifulSoup(html)
code = soup.select("#cnblogs_post_body")
code = str(code[0])
code = re.compile(r'<[^>]+>').sub('', code)
code = code.replace('&lt;','<')
code = code.replace('&gt;','>')
code = code.replace('&amp;','&') #
print code
题目:给出一个数列,要求快速查询区间lr内相同数字的对数。
思路:对于每次询问暴力跑,但是我们注意到由于可以复用之前的结果,所以不同的计算顺序计算量可能不同,直观上来说,两个查询点的曼哈顿距离越短,需要计算的量就越小。于是我们可以找出一个最佳计算顺序,就是平面点阵的最短哈密顿回路。由于这是np的,所以可以用最小曼哈顿生成树代替。再简化一下,可以使用分快处理的方法得到比较快的速度。
这就是所谓的莫队算法。


/*
* @author:  Cwind
*/
///#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-7)
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define FINF (1e3)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (a))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<int,int> P;

const int maxn=5e4+3000;
struct Q{
    int l,r;
    int id;
}q[maxn];;
bool cmp1(const Q &a,const Q &b){return a.r<b.r;}
vector<Q> B[50];
int n,m;
int c[maxn];
ll a[maxn];
ll ans[maxn][2];
void solve(){
    for(int i=1;i<=m;i++) B[q[i].l/1001].pb(q[i]);
    for(int i=0;i<50;i++) sort(B[i].begin(),B[i].end(),cmp1);
    ll ax=0,ay=0;
    int l=1,r=1;
    a[c[1]]++;
    for(int i=0;i<50;i++){
        for(int j=0;j<B[i].size();j++){
            int tl=B[i][j].l,tr=B[i][j].r;
            for(int k=r;k>tr;k--){ax-=a[c[k]]-1;a[c[k]]--;ay-=k-l;}
            for(int k=r+1;k<=tr;k++){ax+=a[c[k]];a[c[k]]++;ay+=k-l;}
            for(int k=l;k<tl;k++){a[c[k]]--;ax-=a[c[k]];ay-=tr-k;}
            for(int k=l-1;k>=tl;k--){ax+=a[c[k]];a[c[k]]++;ay+=tr-k;}
            l=tl,r=tr;
            ll dd=__gcd(ax,ay);
            int id=B[i][j].id;
            ans[id][0]=ax/dd;
            ans[id][1]=ay/dd;
            if(ax==0) ans[id][1]=1;
        }
    }
}
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    cin>>n>>m;
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    solve();
    for(int i=1;i<=m;i++) printf("%lld/%lld\n",ans[i][0],ans[i][1]);
    return 0;

}

View Code

于是我大概知道了我们的原创博客是怎样被那些恶心的网站转走的,但却无能为力。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值