数据库MySQL和Python - KelovpString

/ 0评 / 0

    在上次搞到百度地图API的时候我底下也写了不少检测小程序。在这个过程中我遇到了不少的问题:数据量或者是生产结果很小的时候使用命令行或者用PyCharm的控制台输出是蛮好用还挺酷炫的,但是数据采集我总不能到大后期还是一两条吧,想了这个问题之后我写了一个遍历列表中大学附近网吧个数的程序(我没写正则表达式来匹配//因为不会。。):

# coding=utf-8
import json
from urllib import urlopen, quote
ak = '不给看的Key'
a = ['清华大学','北京大学','北京航空航天大学','北京中医药大学','北京语言大学','北京第二外国语学院','北京工商大学','中央民族大学','首都师范大学',
     '首都医科大学','中国政法大学','中央音乐学院','中央财经大学','中国人民大学','北京师范大学','中央美术学院','中国农业大学','北京外国语大学',
     '北京科技大学','北京邮电大学','北京交通大学','北京化工大学','北京工业大学','北京林业大学','中国地质大学(北京)','北京体育大学','中国石油大学(北京)',
     '中国矿业大学(北京)','华北电力大学(北京)','中国传媒大学','北京理工大学','南开大学','天津大学','天津医科大学','中国民航大学','河北大学','河北工业大学',
     '燕山大学','华北电力大学','石家庄铁道大学','山西大学','太原理工大学','中北大学','内蒙古大学','大连理工大学','辽宁大学','辽宁工程技术大学','大连海事大学',
     '东北大学','中国医科大学',' 东北财经大学','鲁迅美术学院',' 沈阳航空航天大学','沈阳农业大学','大连大学','辽宁师范大学','沈阳工业大学','沈阳药科大学','沈阳建筑大学',
     '吉林大学','延边大学','长春理工大学','东北师范大学','哈尔滨工程大学','哈尔滨工业大学','东北林业大学','东北农业大学','东北石油大学','哈尔滨医科大学','复旦大学',
     '华东理工大学','华东师范大学','上海交通大学','上海财经大学','上海外国语大学','上海大学','东华大学','同济大学','上海对外经贸大学','华东政法大学','上海理工大学',
     '上海海事大学','南京大学','南京农业大学','南京林业大学','南京邮电大学','南京航空航天大学','中国药科大学','中国矿业大学','河海大学','苏州大学','扬州大学','江苏大学',
     '东南大学','江南大学','南京信息工程大学','南京理工大学','南京师范大学','南京工业大学','南京审计学院','南京财经大学','南京医科大学','浙江大学','宁波大学','宁波诺丁汉大学',
     '浙江工业大学','电子科技大学','浙江理工大学','工商大学','师范大学','厦门大学','福州大学','华侨大学','福建农林大学','福建师范大学','安徽大学','中国科学技术大学',
     '安徽工业大学','安徽医科大学','合肥工业大学','安徽农业大学','安徽理工大学','安徽工程大学','安徽师范大学','南昌大学','江西财经大学','江西师范大学','山东大学',
     '中国海洋大学','中国石油大学','山东科技大学','青岛大学','山东师范大学','山东理工大学','青岛科技大学','山东财经大学','青岛理工大学','河南大学','郑州大学',
     '华中科技大学','武汉大学','华中师范大学','华中农业大学','武汉理工大学','中南财经政法大学','中国地质大学(武汉)','中南民族大学','武汉科技大学','湖北大学',
     '武汉工程大学','湖北工业大学','三峡大学','湖南大学','中南大学','湖南师范大学','湖南农业大学','中南林业科技大学','长沙理工大学','湘潭大学','南华大学',
     '湖南科技大学','湖南中医药大学','中山大学','华南农业大学','华南理工大学','华南师范大学','南方医科大学','汕头大学','广州中医药大学','广东外语外贸大学','暨南大学',
     '南方科技大学','广东工业大学','广西大学','海南大学','西南大学','西南政法大学','重庆大学','重庆邮电大学','四川大学','四川农业大学','西南交通大学','电子科技大学',
     '西南财经大学','西南石油大学','四川师范大学',' 成都理工大学',' 西南科技大学','贵州大学','云南大学','昆明理工大学','西北工业大学','西安科技大学','陕西科技大学',
     '西北农林科技大学','西安交通大学','西安电子科技大学','西安外国语大学','陕西师范大学','西北大学','西安建筑科技大学','长安大学','西安邮电大学','西北政法大学',
     '西安石油大学','兰州大学','青海大学','宁夏大学','新疆大学','石河子大学']
for x in range (0,len(a)):
    url = 'http://api.map.baidu.com/geocoder/v2/'
    uri = url + '?address=' + quote(a[x]) +'&output=json&ak='+ak
    res = urlopen(uri).read().decode('utf-8')
    temp = json.loads(res)
    lng = str(temp.get("result").get("location").get("lng"))
    lat = str(temp.get("result").get("location").get("lat"))
    urls = 'http://api.map.baidu.com/place/v2/search?query=网吧&page_size=150&page_num=0&scope=1&location='
    urlk = urls +lat+','+lng+'&output=json&ak=' +ak;
    ress = urlopen(urlk).read().decode('utf-8')
    temps = json.loads(ress)
    lengs = len(temps.get("results"))
    print(a[x]+'附近1000m有 '+str(lengs)+' 家网吧')

    这样一下一运行我发现 一个很严重的问题,我这样输出结果都是在控制台/命令行模式下 ,于是在稍稍改进之后存储到了txt文件 当中。说来也是滑稽,210条结果就弄的很是头大,如果要是数据量再次持续增长那我肯定是要扑街的,而数据库成了我 存储的最佳选择 。但是在这之前我有两个 历程:存储为文件,存储为CVS

存储为媒体文件

    存储媒体文件有两种主要的方式:只获取文件的URL链接,或者把源文件直接下载下来,我们可以通过媒体文件所在的URL来直接引用,这样做有不少优点:

爬虫运行得更快,耗费的流量更少,因为只要链接,不需要下载文件。
可以节省很多存储空间,因为只需要存储 URL 链接就可以。
存储 URL 的代码更容易写,也不需要实现文件下载代码。
不下载文件能够降低目标主机服务器的负载。 

    当然,这种赤裸的下载方式肯定也有不少缺点:

这些内嵌在你的网站或应用中的外站 URL 链接被称为盗链hotlinking), 使用盗链可能会让你麻烦不断,每个网站都会实施防盗链措施。
因为你的链接文件在别人的服务器上,所以你的应用就要跟着别人的节奏运行了。
盗链是很容易改变的。 如果你把盗链图片放在博客上,要是被对方服务器发现,很可能被恶搞。如果你把 URL 链接存起来准备以后再用,可能用的时候链接已经失效了,或者是变成了完全无关的内容。
现实中的网络浏览器不仅可以请求 HTML 页面并切换页面,它们也会下载访问页面上所有的资源。下载文件会让你的爬虫看起来更像是人在浏览网站,这样做反而有好处。

    接下来就是老生常谈了:采集一个网页的一张图片并下载(这里我选取了非js的动态网页//比如bilibili 或者是js https重定向的百度。)

from bs4 import BeautifulSoup
from urllib.request import urlretrieve
from urllib.request import urlopen
html = urlopen('http://store.steampowered.com/')
bsObj = BeautifulSoup(html,"html.parser")
imageLocation = bsObj.find("div", {"class": "responsive_header_logo"}).find("img")["src"]
print(imageLocation)
urlretrieve (imageLocation, "lg.jpg")

    虽然在此只是有针对性的采集了logo,但是爬虫要是这么怂的那还写锤子。继续:

(奥对了,你要是有幸看到角落里的这个垃圾程序的时候别乱换一些危险未知的网址去运行,天知道会下载下来什么,没有任何校检(因为我不会写也看不懂别人的削减)尤其是当你用管理员权限运行这个程序时, 电脑基本已经处于危险之中。如果执行了网页上的一个文件, 那个文件把自己传送到了 ../../../../usr/bin/python 里面, 会发生什么呢?下一次你再运行 Python 程序时,你的电脑就可能会安装恶意软件。 那时候我岂不是很尴尬)

import os
from urllib.request import urlretrieve
from urllib.request import urlopen
from bs4 import BeautifulSoup
downloadDirectory = "downloaded"
baseUrl = "http://www.runoob.com"
def getAbsoluteURL(baseUrl, source):
    if source.startswith("http://www."):
        url = "http://"+source[11:]
    elif source.startswith("http://"):
        url = source
    elif source.startswith("www."):
        url = source[4:]
        url = "http://"+source
    else:
        url = baseUrl+"/"+source
    if baseUrl not in url:
        return None
    return url
def getDownloadPath(baseUrl, absoluteUrl, downloadDirectory):
    path = absoluteUrl.replace("www.", "")
    path = path.replace(baseUrl, "")
    path = downloadDirectory+path
    directory = os.path.dirname(path)
    if not os.path.exists(directory):
        os.makedirs(directory)
    return path
html = urlopen("http://www.runoob.com")
bsObj = BeautifulSoup(html,"html.parser")
downloadList = bsObj.findAll(src=True)
for download in downloadList:
    fileUrl = getAbsoluteURL(baseUrl, download["src"])
    if fileUrl is not None:
        print(fileUrl)
urlretrieve(fileUrl, getDownloadPath(baseUrl, fileUrl, downloadDirectory))

    上面强行用os内库提供了一波支持,os 模块是Python 与操作系统进行交互的接口, 它可以操作文件路径,创建目录,获取运行进程和环境变量的信息,以及其他系统相关的操作。

把数据存储到CVS

    CSVComma-Separated Values,逗号分隔值)是存储表格数据的常用文件格式。 Microsoft Excel 和很多应用都支持 CSV 格式,因为它很简洁。 Python 一样, CSV 里留白( whitespace)也是很重要的:每一行都用一个换行符分隔,列与列之间用逗号分隔( 因此也叫“逗号分隔值”)。 CSV 文件还可以用 Tab 字符或其他字符分隔行,但是不太常见,用得不多。
    Python csv 库可以非常简单地修改 CSV 文件,甚至从零开始创建一个 CSV 文件:

import csv
csvFile = open("../test.csv", 'w+')
try:
    writer = csv.writer(csvFile)
    writer.writerow(('number', 'number plus 2', 'number times 2'))
    for i in range(10):
        writer.writerow( (i, i+2, i*2))
finally:
    csvFile.close()

得到这样一个表格(.cvs)


    Python 新建文件的机制考虑得非常周到( bullet-proof)。如果 ../test.csv不存在, Python 会自动创建文件(不会自动创建文件夹)。如果文件已经存在, Python 会用新的数据覆盖 test.csv 文件。网络数据采集的一个常用功能就是获取 HTML 表格并写入 CSV 文件。
    维基百科的文本编辑器对比词条( https://en.wikipedia.org/wiki/Comparison_of_text_editors) 中用了许多复杂的 HTML 表格, 用到了颜色、链接、排序,以及其他在写入 CSV 文件之前需要忽略的HTML 元素。用 BeautifulSoup get_text() 函数 :

import csv
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("http://en.wikipedia.org/wiki/Comparison_of_text_editors")
bsObj = BeautifulSoup(html)
# 主对比表格是当前页面上的第一个表格
table = bsObj.findAll("table",{"class":"wikitable"})[0]
rows = table.findAll("tr")
csvFile = open("../editors.csv", 'wt', newline='', encoding='utf-8')
writer = csv.writer(csvFile)
try:
    for row in rows:
        csvRow = []
    for cell in row.findAll(['td', 'th']):
        csvRow.append(cell.get_text())
        writer.writerow(csvRow)
finally:
    csvFile.close()

最终正题

    之前对于数据库是有使用过oracle和mysql两个的,这里暂时使用mysql来与python整合使用:但遗憾的是python没有内置支持mysql的库。所以得日常pip一下:

这里给个传送门(我自己用pycharm偷懒的):http://blog.csdn.net/wklken/article/details/7253245

    在完成上面的环境搭建之后就能够进入正题了。(每次搭环境都贼累。希望有时间出一系列软件安装或者是环境搭建的博文)

    我之前课设做医院门诊管理系统遗留下来的孤儿系统的数据库:


import pymysql
conn = pymysql.connect(host='localhost',user='root', passwd='699307', db='hospital',charset="utf8")
cur = conn.cursor()
cur.execute("USE hospital")
cur.execute("SELECT * FROM doctor WHERE no=1000")
print(cur.fetchone())
cur.close()
conn.close()

    这段程序有两个对象:连接对象( conn)和光标对象( cur)。连接 / 光标模式是数据库编程中常用的模式,不过刚刚接触数据库的时候,有些用户很难区分两种模式的不同。 连接模式除了要连接数据库之外,还要发送数据库信息,处理回滚操作(当一个查询或一组查询被中断时,数据库需要回到初始状态,一般用事务控制手段实现状态回滚),创建新的光标对象,等等。而一个连接可以有很多个光标。 一个光标跟踪一种状态state)信息,比如跟踪数据库的使用状态。如果你有多个数据库,且需要向所有数据库写内容,就需要多个光标来处理。光标还会包含最后一次查询执行的结果。 通过调用光标函数,比如 cur.fetchone(),可以获取查询结果。用完光标和连接之后, 千万记得把它们关闭。如果不关闭就会导致连接泄漏connection leak), 造成一种未关闭连接现象, 即连接已经不再使用,但是数据库却不能关闭,因为数据库不能确定你还要不要继续使用它。 这种现象会一直耗费费数据库的资源,所以用完数据库之后记得关闭连接!

    其实路走到这里我们离胜利也就不远了,但是准备工作并未做完,遍历的数据大多很不是你能控制的那种规范格式 ,而我在之前的很多次开发和编程当中字符编码问题一直让我很头疼,所以为了避免待会出现各种奇怪的东西,请创建一个支持unicode编码的库来存放数据。(也可以统一输入输出为utf-8)

    创建一个用于遍历存储的库:


from urllib.request import urlopen

import pymysql as pymysql
from bs4 import BeautifulSoup
import re
import datetime
import random
import pymysql

conn = pymysql.connect(host='localhost',user='root', passwd='699307', db='collecting',charset="utf8")
cur = conn.cursor()
cur.execute("USE collecting")
random.seed(datetime.datetime.now())
def store(title, content):
    cur.execute("INSERT INTO pages (title, content) VALUES (\"%s\",\"%s\")", (title, content))
    cur.connection.commit()
def getLinks(articleUrl):
    html = urlopen("http://en.wikipedia.org"+articleUrl)
    bsObj = BeautifulSoup(html,"html.parser")
    title = bsObj.find("h1").get_text()
    content = bsObj.find("div", {"id":"mw-content-text"}).find("p").get_text()
    store(title, content)
    return bsObj.find("div", {"id":"bodyContent"}).findAll("a",
            href=re.compile("^(/wiki/)((?!:).)*$"))
links = getLinks("/wiki/Kevin_Bacon")
try:
    while len(links) > 0:
        newArticle = links[random.randint(0, len(links)-1)].attrs["href"]
        print(newArticle)
        links = getLinks(newArticle)
finally:
    cur.close()
    conn.close()

进行遍历


    这里有几点需要注意:首先, charset='utf8' 要增加到连接字符串里。这是让连接 conn 把所有发送到数据库的信息都当成 UTF-8 编码格式(当然,前提是数据库默认编码已经设置成 UTF-8)。
    然后要注意的是
store 函数。它有两个参数: title content,并把这两个参数加到了一个 INSERT 语句中并用光标执行,然后用光标进行连接确认。这是一个让光标与连接操作分离的好例子; 当光标里存储了一些数据库与数据库上下文( context)的信息时,需要通过连接的确认操作先将信息传进数据库,再将信息插入数据库。
    最后要注意的是,
finally 语句是在程序主循环的外面,代码的最底下。这样做可以保证,无论程序执行过程中如何发生中断或抛出异常( 当然,因为网络很复杂,你得随时准备遭遇异常),光标和连接都会在程序结束前立即关闭。 (前段时间使用I/O流时惯用的操作。很棒的解决办法呢。)

    还有这个程序的运行并不顺利,环境就整的我够呛,还好工具多网络猛,官方文档:http://legacy.python.org/dev/peps/pep-0249/ 当然我其实开了很多的后门,没有主键且支持可以空的表结构,外加一个申请10000长度的表空间,在一定意义上来讲我已经是失败者了。但是无奈这是现在我能看懂的唯一解决办法了。坑挖好,会来填的//还有其他的遍历。慢慢摸索(话说最近要开始找工作了我真的想先点菜。加油啦。)

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注