爬取百度贴吧(二) --爬取并清洗出所需内容

一、爬取网页源代码

1、下载网页的html源代码

from bs4 import BeautifulSoup

def handlechapter(url):
    response = urllib.request.urlopen(url)
    html = response.read().decode('utf-8')
    return html

html = handlechapter(url)    #url为某贴吧首页的链接
                             #这里为url = 'http://tieba.baidu.com/f?kw=%E4%B8%9C%E5%8C%97%E5%A4%A7%E5%AD%A6%E7%A7%A6%E7%9A%87%E5%B2%9B%E5%88%86%E6%A0%A1&ie=utf-8&pn=0'

注意网页的源代码为utf-8编码

2、将html文件转换为python文件

url_soup = BeautifulSoup(html, 'html.parser')

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag , NavigableString , BeautifulSoup , Comment

【1.Tag类对象】

    即html中的标签,BeautifulSoup对象(以上面代码中的url_soup为例)可以直接在后面接上某种标签的名称,得到的对象是该种标签在这段html中的第一个实例。

    比如对于print(url_soup.h)

                             >>> ... 

(bs里的html对象是可打印的,打印出来就是html原文)

    每个Tag类对象都有两个属性,name和attrs。

    name就是标签的名字,而attrs是个字典,里面记录了所有这个tag的属性值。比如有tag是

HELLO

    其name就是u'h1',而attrs是{u'class':[u'space-seo',u'space-meta'],u'name':u'testname'}  //注意区别tag对象的name属性和写在attrs里面的name属性的区别,另外所有被存到变量里的html内容全部都变成了unicode对象。在print的时候可以指定encode,默认encode是utf-8。还有,所有class属性都默认都被解析成一个列表,即便只有一个class值,也会成为[u'class_value']

    当然,在定位到Tag对象之后可以获取查看其属性值,另一方面,也可以对其属性值等进行修改,修改完了之后就是存在内存中的这个变量里面的,最终可以输出成文件的形式。

【2.String对象】  //selenium用的是text来指代标签内部的文本内容,而bs用的是string。而且对于

这种本身不带文本带后辈节点可能带文本的标签两者意义不同。selenium中的.text会带出来所有后辈节点中的文本,而bs中的这个.string返回的是None

    String对象用于代表每个元素所含的文字部分,在标签后面加上.string即可调用

    注意,某个Tag对象的子节点以及子节点里面的内容都不算进string,当一个元素(比如

这种)里面只有子节点,本身没有文字的话,得到的就是None。

【3.BeautifulSoup对象】

    指代整个文档的对象。可以视为是的对象。其实之前创建的BeautifulSoup对象soup,就是指代整个html文档

【4.comment对象】

    用于代表某个元素内的注释

二、清洗出想要的数据

先上一段单个帖子的格式:

  • 6
    学校周边有什么适合学习的地方吗? 工学馆被封。图书馆人太多
  • 不难发现,我们想要的内容在两个地方:一个是两个标签里的内容(字符串),另一个是标签某个属性的值。

    1、先从简单的看,直接获取标签内部的文本内容:

    我们想要的标题,回复数,最后回复时间都属于这种类型,筛选也很简单,如想获取第一个a标签下的内容

    print(url_soup.a.get_text())
    或
    print(url_soup.a.string)

    但网页源代码中有很多a标签,要找到我们想要的那个需要给它附加一些条件:

    以找置顶帖的个数为例:

    zd_num = 0
    for zd in url_soup.find_all("li", class_="thread_top"):
        zd_num = zd_num + 1

    在bs库中,".find_all"是找所有的此类标签,格式为"find_all("标签",标签属性="***")

    当然find的话就是找第一个,需要注意的是,class属性要写成class_

    同样的,标题和回复数也可以这样得到

    for c in url_soup.find_all("div",class_="j_th_tit"):
        title = c. find_all("a")[0].get_text().strip()
       # print("[",title,"]")
        Title.append(title)
    
    for b in url_soup.find_all("div",class_="j_threadlist_li_left"):
        num = b.find_all("span")[0].get_text().strip()
       # print("回复数:",num)
        Num.append(num)

    这两个都是在div标签下唯一的一个内容,只需要筛选出div,再筛选对应的标签就可以(当然也可以用正则表达式来筛选)

    但是最后回复时间有些不一样,因为在该div下有两个span标签,如:

     起初的想法和上面一样,筛选出这个div-->筛选出想要的span标签-->打印出span下面的字符串

    for a in url_soup.find_all("div", class_="pull_right"):
        aa = a.find_all("span")[1].get_text().strip()
        #time = aa.find(title=re.compile("最后回复时间"))
    
        # aa = a.find("span",class_="threadlist_reply_date pull_right j_reply_data")
        # if (str(aa)=="None"):
        #     time = " "
        # else:
        #     time = aa.get_text().strip()
        # #print(time)
        Time.append(aa)

    前两步都实现了,但是在打印最后字符串的时候却一直出现问题(现在我在怀疑是不是格式错误的问题)。后面突然想到这个div里的span内容都是成对出现,为何不在打印的时候选择一下数组次序呢,索性就不找这个问题了~

    2、选取标签里属性的值

    同样要先从div开始筛选,先选择进入a标签,在获取属性值的时候可以用.get()函数,如下:

    for f in url_soup.find_all("div",class_="j_th_tit"):
        ff = f.a
        fff = ff.get('href')
        #print(fff)
        ffff = "http://tieba.baidu.com"+fff
        Href.append(ffff)

    获得了想要的链接。

    参考了一些很好的博客,附上官方中文文档和一些觉得总结的好的文章:

    BeautifulSoup中文官方文档

    https://www.cnblogs.com/zipon/p/6129280.html

    https://www.cnblogs.com/zhaof/p/6930955.html

    https://www.cnblogs.com/yizhenfeng168/p/6979339.html

    https://www.cnblogs.com/my1e3/p/6657926.html

    最后附上完整代码

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}
    
    from bs4 import BeautifulSoup
    import urllib.request
    import re
    
    Title = []
    Num = []
    Href = []
    Time = []
    
    def handlechapter(url):
        response = urllib.request.urlopen(url)
        html = response.read().decode('utf-8')
        return html
    
    url = 'http://tieba.baidu.com/f?kw=%E4%B8%9C%E5%8C%97%E5%A4%A7%E5%AD%A6%E7%A7%A6%E7%9A%87%E5%B2%9B%E5%88%86%E6%A0%A1&ie=utf-8&pn=0'
    url1 = 'http://tieba.baidu.com/f?kw=%E4%B8%9C%E5%8C%97%E5%A4%A7%E5%AD%A6%E7%A7%A6%E7%9A%87%E5%B2%9B%E5%88%86%E6%A0%A1&ie=utf-8&pn=50'
    
    html = handlechapter(url)
    url_soup = BeautifulSoup(html, 'html.parser')
    
    zd_num = 0
    for zd in url_soup.find_all("li", class_="thread_top"):
        zd_num = zd_num + 1
    
    #print(zd_num)
    for a in url_soup.find_all("div", class_="pull_right"):
        aa = a.find_all("span")[1].get_text().strip()
        #time = aa.find(title=re.compile("最后回复时间"))
    
        # aa = a.find("span",class_="threadlist_reply_date pull_right j_reply_data")
        # if (str(aa)=="None"):
        #     time = " "
        # else:
        #     time = aa.get_text().strip()
        # #print(time)
        Time.append(aa)
    
    for b in url_soup.find_all("div",class_="j_threadlist_li_left"):
        num = b.find_all("span")[0].get_text().strip()
       # print("回复数:",num)
        Num.append(num)
    
    for c in url_soup.find_all("div",class_="j_th_tit"):
        title = c. find_all("a")[0].get_text().strip()
       # print("[",title,"]")
        Title.append(title)
    
    for f in url_soup.find_all("div",class_="j_th_tit"):
        ff = f.a
        fff = ff.get('href')
        #print(fff)
        ffff = "http://tieba.baidu.com"+fff
        Href.append(ffff)
    
    # for x in range(0,50):
    #     print("[",Title[x],"]")
    #     print("回复数:",Num[x])
    #     print(" 链接:",Href[x])
    #     if (x

     

    你可能感兴趣的:(爬虫)