人工智能自动组方实习笔记4—网络爬虫相关python库及python代码实现

本文将记录在人工智能自动组方实习中前期有关于网络爬虫的内容,有如果有哪位小伙伴在做相关内容,可以发邮件联系([email protected])与我互相学习互撩互喷

在前期准备工作中,需要得到中医相关的大量数据包括中医药材,药方,疾病,及疾病处理等多个数据集。对于疾病数据库将某本中医书籍上的疾病信息通过人工处理和OCR加工,对于中医药材药方数据集采用网络爬虫从中医相关网站爬取,对于疾病处理数据库采用人工实体标注将每一个疾病打上大类(例如 月经病 带下病 妊娠病)和病机(例如气血两虚 肾阴不足 )两个标签为以后使用,对于药材和药方两个数据库我们采用的是通过网络爬虫爬取相关专业网站并保存。

网络爬虫的流程:

将HTML文件利用beautifulsoup库转为BS对象方便对HTML标签进行查找,利用re正则化快速找到相同格式的内容,并利用OS,pandas库将其保存。
在本次实习中网络爬虫的具体流程如下:
1. 使用selenium中的模拟浏览为登陆网页或者使用urllib库得到HTML文件并且将每一HTML文件保存在TXT文件中。具体代码详见下文
2. 将保存的HTML文件进行判断,将不能打开的HTML的文件并保存下来,具体代码详见下文
3. 对无法打开的HTML文件重新登录操作并保存HTML,具体代码详见下文
4. 将得到都可以打开的HTML文件根据HTML标签和CSS文件抽取信心并保存在csv文件中,具体代码详见下文

下面主要介绍下用到的几个python库文件及参考博文。

python库

urllib

在网上爬取内容,首先我们需要得到网址对应的HTML文件,在这里采用的的是urllib库。urllib库几乎可以对HTTP进行所有操作,我们只使用了 u r l l i b . r e q u e s t 请求模块,具体内容我参考的是这2篇博文urllib基本使用方法python3中的urllib使用

bs4

bs4库中的Beautifulsoup将HTML文件解析为BS对象,通过函数更方便对HTML文件进行查找操作,具体内容我参考的是这2篇博文BeautifulSoup简介1BeautifulSoup简介2

re

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
我使用的正则库的相关内容都是参考这个网上这篇博文正则表达式

selenium

一个非常强大的python自动化测试库
有些时候需模拟浏览器登陆,并且保持cookies值使得操作不掉线,在这里我们使用的是selenium模块,selenium是一个自动化测试工具,支持各种浏览器包括chrome,safari,ie以及 PhantomJS(headless webdriver)等模拟浏览器操作,其中PhantomJS 用来渲染解析JS,Selenium 用来驱动以及与 Python 的对接,Python 进行后期的处理。
在浏览器上我们选择的是PhantomJS这个无头浏览器,因为在模拟浏览器登陆时使用其他浏览器会自动打开浏览器完成对应的操作,而PhantomJS只是在后台操作并没有显示所有效率很高,使用无头浏览器时需要将PhantomJS的保存位置写进系统环境变量中或者在使用时声明所浏览器所在地址例如

from selenium import webdriver
driver = webdriver.PhantomJS(executable_path="phantomjs.exe")

因为是模拟浏览器操作,selenium模块也可以操作http的所有操作。具体内容我参考的是selenuim用法1selenium用法2PhantomJS

利用无头浏览器登录网页并保存HTML文件

登陆网站并保持cookies,将HTML文件保存下来,为下一步提取信息

import time
import re
import random
from selenium import webdriver
#登录获取并保持cookies
def login(username, password):
    url = 'https://www.yaozh.com/login'
    # browser = webdriver.PhantomJS()
    browser = webdriver.Firefox()
    browser.get(url)
    # print driver.title
    name_input = browser.find_element_by_id('username')  # 找到用户名的框框
    pass_input = browser.find_element_by_id('pwd')  # 找到输入密码的框框
    login_button = browser.find_element_by_id('button')  # 找到登录按钮
    name_input.clear()
    name_input.send_keys(username)  # 填写用户名
    time.sleep(0.2)
    pass_input.clear()
    pass_input.send_keys(password)  # 填写密码
    time.sleep(0.2)
    login_button.click()            # 点击登录
    time.sleep(1)
    # time.sleep(0.2)
    browser.get('https://db.yaozh.com/')
    browser.get('https://db.yaozh.com/zhongyaocai')
    print('登录成功')
    # print(browser)
    return browser
#爬取页面
def spider(browser,url,page):
    t = page
    for i in range(t, 10027500):
        while True:
            # time.sleep(1)
            try:
                browser.get(url + str(i)+'.html')
            except:
                return page
            if ('<title>跳转提示</title>' in browser.page_source) or('暂无权限' in browser.page_source) or('<body onload="challenge();">' in browser.page_source):
                print("错误!")
                continue
            f = open('data/fangjipage1/'+str(i)+'.txt','a',encoding='utf-8')
            f.write(browser.page_source)
            print(i)
            f.close()
            break
        page += 1

if __name__ == "__main__":
    global page
    page = 1002400
    user = "zhaoliang123"
    pw = "zhaoliang123"
    url = 'https://db.yaozh.com/fangji/'
    for i in range(100):
        time.sleep(1)
        browser = login(user, pw)
        print('当前页'+str(page))
        if page==100275000:
            break
        page = spider(browser, url,page)

判断保存的HTML是否可以打开

将每一保存的HTML文件进行判断是否都可以打开,将不能打开的HTML文件保存下来重新操作

import re
import os
#判断页面是否爬取失败,'data/fail.txt'用来存放无效页面的序号
def judge(savepath,datapath,startpage,endpage):
    f = open(savepath, 'a', encoding='utf-8')
    for i in range(startpage, endpage+1):
        path = datapath+str(i)+'.txt'
        # print(path)
        if os.path.exists(path):
            data = open(path, 'r', encoding='utf-8')
            string = data.read()
            if ('暂无权限' in string):
                # print(str(i) + '.html\n')
                f.write(str(i) + '.html\n')
                print('暂无权限问题'+str(i))
            if ('404 - 药智数据库' in string):
                f.write(str(i) + '.html\n')
                print('404问题'+str(i))
            if ('这个是服务器压力过大的临时跳转页面' in string):
                print('跳转提示问题'+str(i))
                f.write(str(i) + '.html\n')
            data.close()
    f.close()
def get_index(datapath):
    startpage = min(int(re.sub('.txt','',f)) for f in os.listdir(datapath))
    endpage = max(int(re.sub('.txt','',f)) for f in os.listdir(datapath))
    print(startpage,endpage)
    return startpage,endpage
if __name__ == "__main__":
    #fail.txt 用来存放无效网页的序号
    savepath = 'data/fail.txt'
    #存放已爬网页的文件夹
    datapath = 'data/fangjipage/'
    #获得网页序号范围
    startpage,endpage = get_index(datapath)
    judge(savepath, datapath, startpage, endpage)

对无法打开的HTML文件进行重新操作并保存

import time
import re
import random
from selenium import webdriver
#登录获取并保持cookies
def login(username, password):
    url = 'https://www.yaozh.com/login'
    browser = webdriver.PhantomJS()
    # browser = webdriver.Firefox()
    browser.get(url)
    name_input = browser.find_element_by_id('username')  # 找到用户名的框框
    pass_input = browser.find_element_by_id('pwd')  # 找到输入密码的框框
    login_button = browser.find_element_by_id('button')  # 找到登录按钮
    name_input.clear()
    name_input.send_keys(username)  # 填写用户名
    time.sleep(0.2)
    pass_input.clear()
    pass_input.send_keys(password)  # 填写密码
    time.sleep(0.2)
    login_button.click()            # 点击登录
    time.sleep(1)
    print('登录成功')
    return browser
#爬取方剂页面,每爬取一次随机休眠几秒
def spider(browser,url,urlfile,savepath):
    f = open(urlfile,'r',encoding='utf-8')
    i = 1
    for u in f.readlines():
        while True:
            time.sleep(int(random.uniform(2,5)))
            browser.get(url+re.sub('\n','',u))
            if ('<title>跳转提示</title>' in browser.page_source)or('<body onload="challenge();">' in browser.page_source):
                print("错误!重新爬取")
                continue
            if ('404 - 药智数据库' in browser.page_source):
                print("错误!重新爬取")
                continue
            #失去权限直接退出重新爬取
            if ('暂无权限' in browser.page_source):
                print('失去权限,'+u+'爬取失败,请删除已爬取过的序号并重新爬取')
                return
            #保存路径
            f = open(savepath+re.sub('.html|\n','',u)+'.txt','w',encoding='utf-8')
            f.write(browser.page_source)
            f.close()
            break
        print(i,u+'爬取成功')
        i += 1
if __name__ == "__main__":
    #用户名及密码
    user = "zhaoliang123"
    pw = "zhaoliang123"
    #要爬取的url
    url = 'https://db.yaozh.com/fangji/'
    #存放无效网页序号的文件路径
    urlfile = 'data/fail.txt'
    #存放爬取网页的文件夹,预先创建好文件夹!
    savepath = 'data/newpage/'
    browser = login(user, pw)
    spider(browser, url, urlfile, savepath)

得到网站Html文件根据标签和CSS文件提取信息

对得到网址的html文件,根据标签和css提取信息并保存在csv中

from bs4 import BeautifulSoup
import pandas as pd
import re
import os
def extract(page_file,startpage,endpage):
    # 存放需要的保存的属性
    column = [
        '方名', '规范名', '经典', '出处', '功用大类',
        '功用小类', '处方', '炮制', '功用', '主治',
        '方解', '禁忌', '化裁', '附方', '文献', '序号'
    ]
    #创建DataFrame
    df = pd.DataFrame(columns=column)
    #根据方剂含有的属性为其构造Series并插入dataframe
    for n in range(startpage,endpage+1):
        path = page_file+str(n)+'.txt'
        if os.path.exists(path):
            # d保存方剂数据,th保存网页元素th中的数据,td保存td中的数据
            th = []
            td = []
            d = {
                '方名': '', '规范名': '', '经典': '', '出处': '', '功用大类': '',
                '功用小类': '', '处方': '', '炮制': '', '功用': '', '主治': '',
                '方解': '', '禁忌': '', '化裁': '', '附方': '', '文献': '', '序号': ''
            }
            f = open(path,'r',encoding='utf-8')
            html = f.read()
            f.close()
            #寻找网页中的th元素(方剂属性)和td元素(属性的内容)
            bs = BeautifulSoup(html)
            thresult = bs.find_all('th')
            tdresult = bs.find_all('td')
            # 将表格内数据存入列表
            for r in thresult:
                th.append(r.get_text())
            for r in tdresult:
                td.append((re.sub('\n','',r.get_text())).strip())
            #判断该方剂存在哪些属性,将其索引存入exist
            exist = []
            for i in range(len(th)):
                if th[i] in column:
                    exist.append(i)
            #去掉多余的属性
            t = th
            for i in range(len(exist)):
                th[i] = t[exist[i]]
            t = td
            for i in range(len(exist)):
                td[i] = t[exist[i]]
            #规整该方剂的数据
            for i in range(len(th)):
                d[th[i]] = td[i]
            d['序号'] = str(n)
            #插入dataframe
            data = pd.Series(d)
            df = df.append(data,ignore_index=True)
            print(n, d)
    return df

#调整属性顺序
def att_order(df):
    cols = list(df)
    cols.insert(0, cols.pop(cols.index('序号')))
    cols.insert(1, cols.pop(cols.index('方名')))
    cols.insert(2, cols.pop(cols.index('规范名')))
    cols.insert(3, cols.pop(cols.index('经典')))
    cols.insert(4, cols.pop(cols.index('出处')))
    cols.insert(5, cols.pop(cols.index('主治')))
    cols.insert(6, cols.pop(cols.index('功用')))
    cols.insert(7, cols.pop(cols.index('功用大类')))
    cols.insert(8, cols.pop(cols.index('功用小类')))
    cols.insert(9, cols.pop(cols.index('处方')))
    cols.insert(10, cols.pop(cols.index('炮制')))
    cols.insert(11, cols.pop(cols.index('方解')))
    cols.insert(12, cols.pop(cols.index('禁忌')))
    cols.insert(13, cols.pop(cols.index('化裁')))
    cols.insert(14, cols.pop(cols.index('附方')))
    cols.insert(15, cols.pop(cols.index('文献')))
    df = df.ix[:, cols]
    return df

def get_index(datapath):
    startpage = min(int(re.sub('.txt', '', f)) for f in os.listdir(datapath))
    endpage = max(int(re.sub('.txt', '', f)) for f in os.listdir(datapath))
    # print(startpage,endpage)
    return startpage, endpage


if __name__ == "__main__":
    #存放爬取网页的文件夹
    page_path = 'data/fangjipage/'
    #获得文件夹下网页的序号范围
    startpage,endpage = get_index(page_path)
    #参数:网页文件夹,起始页,结束页
    df = extract(page_path,startpage,endpage)
    #对属性进行排序
    df = att_order(df)
    #导出csv
    df.to_csv('1002400_10027500.csv')

猜你喜欢

转载自blog.csdn.net/qq_22235017/article/details/80974189