家长帮分板块爬取

导言

本次爬取对象为上海家长帮bbs论坛,从论坛构造看出,其网站分为不同板块,每个板块有名称,主题数和帖子数,如下图所示

在这里插入图片描述
每个板块的网页结构都差不多,只是初始页不同而已,比如预处年级板块的初始网页为http://www.jzb.com/bbs/forum-653-1.html 而初中年级板块的网址变为http://www.jzb.com/bbs/forum-1235-1.html 仔细的你也许发现从一个板块切换到另一个板块只是url里面的参数从653变成了1235, 好了,了解初始网页入口的构造后,我们就拿预处年级这个板块来练手,切换到其他板块的时候把start_url相应切换便是。

完整代码

# -*- coding: utf-8 -*-
"""
Created on Sun Aug 11 15:32:44 2019
project name:jzb
@author: 帅帅de三叔
"""
import requests #导入服务器请求模块
import re #导入正则模块
import time 
from bs4 import BeautifulSoup #导入网页解析模块
import pymysql #导入数据库模块
header={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"} #构造请求头
db=pymysql.connect("localhost","root","123456awd","jzb",charset='utf8') #链接数据库
cursor=db.cursor() #获取游标

# ==================================华丽分割线===========================================
# cursor.execute("drop table if exists sh_yuchu")
# c_sql="""create table sh_yuchu(
#              user varchar(20),
#              level varchar(15),
#              currency varchar(5),
#              comment varchar(2000),
#              date varchar(30),
#              email varchar(50)
#              )Engine=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=UTF8"""
# cursor.execute(c_sql)
# ===========================华丽分割线==================================================

def generate_url(num): #定义构造所有一级网页函数
    start_url="http://www.jzb.com/bbs/forum-653-{}.html" #起始网页
    for page in range(57,num):
        yield start_url.format(page)

def get_items(generate_url): #定义获取数据函数
    response=requests.get(generate_url,headers=header) #带头请求网页
    time.sleep(1)
    cook=BeautifulSoup(response.text,'lxml') #解析网页
    tbody=cook.findAll("a",class_="xst") #找出所有话题tl bm bmw brtn find("div",class_="xst thread-name")
    #print(tbody[0:20])
    for body in tbody:
        detail_url=body['href'] #话题链接
        #print(detail_url) #测试话题链接
        answer=requests.get(detail_url,headers=header,timeout=10) #对话题页请求
        time.sleep(1)
        soup1=BeautifulSoup(answer.text,'lxml') #解析网页
        try:
            pattern_=re.compile("\d+") #用以正则数字
            total_page=int(re.search(pattern_,soup1.find("span",class_="pgt").find("span",class_="eduu_pg").find("label").get_text()).group(0)) #话题总页数
        except:
            total_page=1
        #print(total_page)
        for page in range(1,total_page+1):
            direct_url=detail_url.split("-")[0]+"-"+detail_url.split("-")[1]+"-"+str(page)+"-"+"1.html" #利用总页数构造详情页
            print("详情页:",direct_url)
            response=requests.get(direct_url,headers=header,timeout=30) #发出请求
            time.sleep(1)
            soup=BeautifulSoup(response.text,'lxml') #解析网页
            comment_box=soup.findAll("div",class_="eduu_brg eduu_thread_Box") #评论框
            for box in comment_box:
                #print(box)
                try:
                    user=box.find("a",class_="xw1 Tt_ifo_name").get_text() #用户名
                except:
                    user=""
                try:
                    level=box.find("a",class_="ifo_icon name_icon").get_text() #作者
                except:
                    level=""
                try:
                    currency=box.find("a",class_="ifo_icon yuanbao_icon").get_text() #元宝数
                except:
                    currency=""
                try:
                    comment=box.find("div",class_="t_fsz").find("td",class_="t_f").get_text().strip().replace(" ","") #评论内容
                    if len(comment)>300:
                        comment=comment[:300]
                    else:
                        comment=comment
                except:
                    comment=""
                try:
                    pattern=re.compile(r'[0-9a-zA-Z._]+@[0-9a-zA-Z._]+\.[0-9a-zA-Z._]+')  #邮箱格式
                    email=re.search(pattern,comment).group(0).strip().replace(" ","")
                except:
                    email=""
                try:
                    date=box.find("div",class_="right_Box").find("div",class_="eduu_rept_rt").find("em").get_text() #评论时间
                except:
                    date=""
                #print(user,level,currency,comment,date,email)
                insert_data=("insert into sh_yuchu(user,level,currency,comment,date,email)""values(%s,%s,%s,%s,%s,%s)")
                jzb_data=([user,level,currency,comment,date,email])
                cursor.execute(insert_data,jzb_data)
                db.commit()
                s = requests.session()
                s.keep_alive = False #关闭多余的连接
if __name__=="__main__":
    num=int(input("please input the total page:")) #请输入总网页数
    for page in generate_url(num):
        print("一级网页:",page)
        get_items(page)

代码解读

首先,网络爬虫不能给人家服务器造成太大压力,可以 time.sleep() 挂起进程一会会,为了使得爬虫代码的泛化能力增强,首先设置一个初始网页变量 start_url,如果你要爬取其他板块只需要把代码里面的start_url相应切换便是,接着利用generate_url() 函数把改板块下的所有一级网页都构造出来,构造出来的一级网页作为 get_items() 的形式参数,便进入一级网页,一级网页有好几十个主题(话题),首先要做的是获取每个话题的链接,这里是利用网页url的特点通过字符串拼接 detail_url.split("-")[0]+"-"+detail_url.split("-")[1]+"-"+str(page)+"-"+“1.html”,其中page数就是上面一步首先要获取的,这样就把所有话题下面的详情网址 detail_url 构造出来了,接下来任务便是进入direct_url 拎取所需的字段,如 用户名,级别,元宝数,评论内容,评论时间等,倘若内容里面含有邮箱还可以用正则方法正则出来单独作为一个字段,由于会有空值出现,这里采用 try: except: 格式控制,最后把取下来的数据存入数据库。在最后定义了一个主函数 main() ,由于每次爬取需要进入3级网页,所以还是要分批爬取,不能一口吃个大胖子,首次爬取的时候要在数据库里面创建表 sh_yuchu,在后面的爬取需要注释掉 华丽分割线 里面的代码,这段代码是重新建表的意思。

# cursor.execute("drop table if exists sh_yuchu")
# c_sql="""create table sh_yuchu(
#              user varchar(20),
#              level varchar(15),
#              currency varchar(5),
#              comment varchar(2000),
#              date varchar(30),
#              email varchar(50)
#              )Engine=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=UTF8"""
# cursor.execute(c_sql)

留了一个输入总共爬取的一级网址数量 num
mysql截图

遇到问题及处理办法

多次出现运行到半路就出现下面的错误代码

ConnectionError: HTTPConnectionPool(host='www.jzb.com', port=80): Max retries exceeded with url: /bbs/thread-2569529-57-1.html (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x000002371295E630>: Failed to establish a new connection: [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。'))

很可能请求过于频繁,很多requests请求还没关闭又发出新的请求导致服务器响应不过来,将前面的requests设置关闭即可。在db.commit() 后面加上如下代码即可,但此方法并非一劳永逸解决此问题。

cursor.execute(insert_data,jzb_data)
db.commit()
s = requests.session()
s.keep_alive = False #关闭多余的连接

免责声明

Python爬虫仅为学习交流,如有冒犯,请告知删。

延申阅读
青客公寓挂牌房源分城市爬取
优客逸家挂牌房源爬取
建方公寓挂牌房源信息爬取
江寓租房挂牌房源信息爬取
通过关键词获取微博内容
孩子数学有问题,找三行呀!

发布了45 篇原创文章 · 获赞 12 · 访问量 8674

猜你喜欢

转载自blog.csdn.net/zengbowengood/article/details/99704573
今日推荐