初试python爬虫实战三——爬取考研帮导师简介

1 基础版本

1.1 抓取首页链接,依次访问

与前两次实战的区别不大,所以这次尝试模块化编程。不再写之前脚本式的代码。写完代码之后来写的博客,所以只简单记一下编码过程中遇到的问题以及解决方案。

  • requests请求返回的数据出现了中文乱码
    仔细分析了一下页面,考研帮该页面的源代码为:
 <html lang="zh">
  <head>
    ...

猜想还是编码的问题。所以在requests的请求中,最终返回做一个编码处理:

 # 出现中文乱码的解决方法
    resp.encoding = 'utf-8'
  • 取出的数据中包含大量的‘\r’ '\t' '\u' '\u3000'等控制字符。解决办法为在requests之后,返回数据之前做一个.replece处理:
 resp = resp.text.replace('\t', '')
              .replace('\r', '')
              .replace('\n', '').replace('\u3000', '')

基础版本首页最终代码:

import time
import requests
from lxml import etree

start_url = 'http://www.kaoyan.com/daoshi/'
titles = []
context = []
data = {}


# 访问
def get_resp(url):
    ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' \
         'AppleWebKit/537.36 (KHTML, like Gecko) ' \
         'Chrome/80.0.3987.116 Safari/537.36'
    header = {'User-Agent': ua}
    resp = requests.get(url, headers=header)
    # 出现中文乱码的解决方法
    resp.encoding = 'utf-8'
    if resp.status_code == 200:
        # return resp.text
        resp = resp.text.replace('\t', '').replace('\r', '').replace('\n', '').replace('\u3000', '')
        return resp
    else:
        print(url + '访问失败')


def parse_teacher(resp_page):

    et = etree.HTML(resp_page)
    if et is not None:
        # 处理标题
        selectors = et.xpath('//h1')
        titles.append(selectors[0].text)

        # 处理详情介绍
        selectors2 = et.xpath("//div[@class='articleCon']//p/text()")
        text = ''
        for s in selectors2:
            text += s
        context.append(text)

    else:
        print('发现一个异常页面,已跳过')


# 处理链接,依次访问超链接
def link_parse(resp):
    et = etree.HTML(resp)
    links = et.xpath("//ul[@class='list areaZslist']/li//a/@href")
    for link in links:
        resp_page = get_resp(link)
        parse_teacher(resp_page)


def make_and_print():
    data.update(zip(titles, context))
    print(data)


if __name__ == '__main__':
    start_time = time.time()

    # 访问初始url
    resp = get_resp(start_url)
    # 处理链接 循环访问下载
    link_parse(resp)
    # 处理最终数据并输出
    make_and_print()

    last_time = time.time() - start_time
    print(last_time)

输出效果为:


V1效果

1.2 获取下一页

这种数据值抓取一页当然是毫无意义的,下面要做的是检查页面是否存在下一页,如果存在,再继续加载下一页。核心代码为:

# 多页处理
    next_url = et.xpath('//div[4]/a[11]/@href')
    if next_url:
        print('下一页地址:', next_url[0])
        r = get_resp(next_url[0])
        link_parse(r)
    else:
        print('页面加载完毕,开始逐个下载导师资料,请稍后...')

1.3 保存到记事本

以title为文件名,将各个导师信息保存到记事本中。

def save_data(path, dicta):
    if not os.path.exists(path):
        os.mkdir(os.getcwd() + '\\data_out')
    os.chdir(path)
    for k, v in dicta.items():
        filename = k+'.txt'
        file_context = v
        f = open(filename, 'w+', encoding='utf-8')
        f.write(file_context)
        f.seek(0)
        f.close()
        print(k, '资料保存完成!')

实现的效果为:


最终效果1
最终效果2
最终效果3

完美实现需求。第一部分结束。第一部分最终完整代码:

import time
import os
import requests
from lxml import etree

start_url = 'http://www.kaoyan.com/daoshi/'
titles = []
context = []
data = {}


# 访问
def get_resp(url):
    ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' \
         'AppleWebKit/537.36 (KHTML, like Gecko) ' \
         'Chrome/80.0.3987.116 Safari/537.36'
    header = {'User-Agent': ua}
    resp = requests.get(url, headers=header)
    # 出现中文乱码的解决方法
    resp.encoding = 'utf-8'
    if resp.status_code == 200:
        # return resp.text
        resp = resp.text.replace('\t', '') \
            .replace('\r', '') \
            .replace('\n', '') \
            .replace('\u3000', '') \
            .replace('\xa0', '')
        return resp
    else:
        print(url + '访问失败')


def parse_teacher(resp_page):
    et = etree.HTML(resp_page)
    if et is not None:
        # 处理标题
        selectors = et.xpath('//h1')
        titles.append(selectors[0].text)

        # 处理详情介绍
        selectors2 = et.xpath("//div[@class='articleCon']//p/text()")
        text = ''
        for s in selectors2:
            text += s
        context.append(text)

    else:
        print('发现一个异常页面,已跳过')


# 处理链接,依次访问超链接
def link_parse(resp):
    et = etree.HTML(resp)
    links = et.xpath("//ul[@class='list areaZslist']/li//a/@href")
    # 多页处理
    next_url = et.xpath('//div[4]/a[11]/@href')
    if next_url:
        print('下一页地址:', next_url[0])
        r = get_resp(next_url[0])
        link_parse(r)
    else:
        print('页面加载完毕,开始逐个下载导师资料,请稍后...')
    for link in links:
        resp_page = get_resp(link)
        parse_teacher(resp_page)


def make_and_print():
    data.update(zip(titles, context))
    print(data)


def save_data(path, dicta):
    if not os.path.exists(path):
        os.mkdir(os.getcwd() + '\\data_out')
    os.chdir(path)
    for k, v in dicta.items():
        filename = k+'.txt'
        file_context = v
        f = open(filename, 'w+', encoding='utf-8')
        f.write(file_context)
        f.seek(0)
        f.close()
        print(k, '资料保存完成!')


if __name__ == '__main__':
    start_time = time.time()
    # save_data('./data_out', data)

    # 访问初始url
    resp = get_resp(start_url)
    # 处理链接 循环访问下载
    link_parse(resp)
    # 处理最终数据并输出
    make_and_print()
    save_data('./data_out', data)

    print(len(titles))
    last_time = time.time() - start_time
    print(last_time)

2.多线程版

虽然数据量比较小,耗费的时间也并不多。但是觉得前面的多线程还是没怎么理解,再写一个demo试试吧~
首先导入threading系统库。注意ing。导入Queue用于存放链接队列。

import threading

定义一个变量,控制线程个数。定义一个线程池,保存线程。定义一个链接队列

thread_num = 10
threads = []
links_queue = Queue()

在链接处理函数中,将所有链接存入队列。

 for link in links:
        links_queue.put(link)

在主函数中创建线程

 for t in range(thread_num):
        t = threading.Thread(target=download)
        t.start()
        threads.append(t)

编写download()方法

def download():
    while True:
        link = links_queue.get()
        if link is None:
            break
        resp_page = get_resp(link)
        parse_teacher(resp_page)
        print('当前下载线程数:%s,剩余%s条链接未解析' %
              (len(threading.enumerate())-1, links_queue.qsize()))

依次退出线程

    for i in range(thread_num):
        links_queue.put(None)
    for t in threads:
        t.join()

实际效果明显增快

效果1
效果2
效果3

与单线程相比,增速一倍以上!

贴出多线程版完整代码:

# 多线程爬取导师信息
import time
import os
import threading
from queue import Queue
import requests
from lxml import etree

start_url = 'http://www.kaoyan.com/daoshi/'
titles = []
context = []
data = {}

thread_num = 10
threads = []
links_queue = Queue()


# 访问
def get_resp(url):
    ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' \
         'AppleWebKit/537.36 (KHTML, like Gecko) ' \
         'Chrome/80.0.3987.116 Safari/537.36'
    header = {'User-Agent': ua}
    resp = requests.get(url, headers=header)
    # 出现中文乱码的解决方法
    resp.encoding = 'utf-8'
    if resp.status_code == 200:
        # return resp.text
        resp = resp.text.replace('\t', '') \
            .replace('\r', '') \
            .replace('\n', '') \
            .replace('\u3000', '') \
            .replace('\xa0', '')
        return resp
    else:
        print(url + '访问失败')


def parse_teacher(resp_page):
    et = etree.HTML(resp_page)
    if et is not None:
        # 处理标题
        selectors = et.xpath('//h1')
        titles.append(selectors[0].text)

        # 处理详情介绍
        selectors2 = et.xpath("//div[@class='articleCon']//p/text()")
        text = ''
        for s in selectors2:
            text += s
        context.append(text)

    else:
        print('发现一个异常页面,已跳过')


# 处理链接,依次访问超链接
def link_parse(resp):
    et = etree.HTML(resp)
    links = et.xpath("//ul[@class='list areaZslist']/li//a/@href")
    # 多页处理
    next_url = et.xpath('//div[4]/a[11]/@href')
    if next_url:
        print('下一页地址:', next_url[0])
        r = get_resp(next_url[0])
        link_parse(r)
    else:
        print('页面加载完毕,开始逐个下载导师资料,请稍后...')
    for link in links:
        links_queue.put(link)
        # resp_page = get_resp(link)
        # parse_teacher(resp_page)


def make_and_print():
    data.update(zip(titles, context))
    print(data)


def save_data(path, dicta):
    if not os.path.exists(path):
        os.mkdir(os.getcwd() + '\\data_out')
    os.chdir(path)
    for k, v in dicta.items():
        filename = k+'.txt'
        file_context = v
        f = open(filename, 'w+', encoding='utf-8')
        f.write(file_context)
        f.seek(0)
        f.close()
        print(k, '资料保存完成!')


def download():
    while True:
        link = links_queue.get()
        if link is None:
            break
        resp_page = get_resp(link)
        parse_teacher(resp_page)
        print('当前下载线程数:%s,剩余%s条链接未解析' %
              (len(threading.enumerate())-1, links_queue.qsize()))


if __name__ == '__main__':
    start_time = time.time()
    # 访问初始url
    resp = get_resp(start_url)
    # 处理链接 循环访问下载
    link_parse(resp)

    for t in range(thread_num):
        t = threading.Thread(target=download)
        t.start()
        threads.append(t)

    for i in range(thread_num):
        links_queue.put(None)
    for t in threads:
        t.join()

    # 处理最终数据并输出
    make_and_print()
    save_data('./data_out', data)

    last_time = time.time() - start_time
    print('共下载%s条导师信息,耗时%s秒' % (len(titles), last_time))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容