1. 程式人生 > >python網路資料採集-第5章儲存資料

python網路資料採集-第5章儲存資料

5.1 媒體檔案簡述

網路上的資源很多,有圖片,視訊,常規檔案rar\zip等,由於網路爬去的資料量大,如果直接儲存,相對只儲存對應的連結字串,有很多缺陷:1、由於下載,導致爬取速度慢;2、消耗儲存空間;3、而且還要實現檔案下載的方法,繁瑣;優點:1、防止由於盜鏈改變導致的資訊丟失(盜鏈即網頁內部通往外部網頁的連結),盜鏈所連線的資訊可能變化

簡單地檔案下載

# 1、單個檔案下載
from urllib.request import urlretrieve
from urllib.request import urlopen
from bs4 import BeautifulSoup


'''
爬去網頁示例內容
<a href="/" id="logo" rel="home" title="Home">
    <img alt="Home" src="http://www.pythonscraping.com/sites/default/files/lrg_0.jpg"/>
'''
html = urlopen("http://www.pythonscraping.com") bsObj = BeautifulSoup(html,"html5lib") print(bsObj) # 注意這裡find格式對映關係 imageLocation = bsObj.find("a",{"id":"logo"}).find("img")["src"] print(imageLocation) # 下載圖片 urlretrieve(imageLocation,"img01.jpg")
# 批量爬取檔案並下載
import os
from urllib.request import
urlopen from urllib.request import urlretrieve from bs4 import BeautifulSoup ''' 爬去網頁示例內容 <a href="/" id="logo" rel="home" title="Home"> <img alt="Home" src="http://www.pythonscraping.com/sites/default/files/lrg_0.jpg"/> ''' # 為何這裡寫的如此複雜? # 在於我們希望將下載的資源根據所在的主url,來進行相對應的資料夾 baseurl = "http://pythonscraping.com"
downloadDirectory = "download" html = urlopen("http://www.pythonscraping.com") bsObj = BeautifulSoup(html, "html5lib") downloadList = bsObj.findAll(src=True) print(len(downloadList)) # 獲取資料夾絕對路徑的url def getAbsoluteURL(baseUrl, downloadUrl): url = "" if downloadUrl.startswith("http://www."): url = "http://" + downloadUrl[11:] if baseurl not in url: return None return url # 獲取資料夾絕對路徑(用於地址儲存) def getDownloadPath(baseurl, fileurl, downloadDirectory): # 含有?的需要刪除?之後的字串(下同) if "?" in fileurl: endIndex = fileurl.index("?") fileurl = fileurl[:endIndex] path = fileurl.replace(baseurl, "") path = downloadDirectory + path directory = os.path.dirname(path) if not os.path.exists(directory): os.makedirs(directory) return path for download in downloadList: fileurl = getAbsoluteURL(baseurl, download["src"]) # 'NoneType' object has no attribute 'replace' if fileurl is not None: if "?" in fileurl: endIndex = fileurl.index("?") fileurl = fileurl[:endIndex] print(fileurl) urlretrieve(fileurl, getDownloadPath(baseurl, fileurl, downloadDirectory))

5.2 把資料儲存到csv中

這裡的意思是將Html中的表格資料儲存到csv表格中。1、找到一個table標籤及其對應的id;2、找到所有的tr標籤,即所有的行資訊;3、遍歷所有的行資訊,找到每行對應的td或者th標籤,寫入csv中

# 例1:簡單地csv檔案寫入
# 檔案如果不存在會自動建立
import csv
csvFile = open("files/test.csv","w+")
try:
    writer = csv.writer(csvFile)
    writer.writerow(('number1','number2','number3'))
    for i in range(10):
        writer.writerow((i,i+2,i*2))
finally:
    csvFile.close()


# 例2,網頁資訊寫入csv
from urllib.request import urlopen
from bs4 import BeautifulSoup
import codecs

html = urlopen("https://baike.baidu.com/item/%E5%9B%BD%E5%86%85%E7%94%9F%E4%BA%A7%E6%80%BB%E5%80%BC/31864?fromtitle=GDP&fromid=41201")
bsObj = BeautifulSoup(html,"html5lib")

# print(bsObj)

table = bsObj.findAll("table",{"class":"table-view log-set-param"})[1]
rows = table.findAll("tr")

'''
excel開啟csv檔案,可以識別編碼“GB2312”,但是不能識別“utf-8”,資料庫裡的字串編碼是utf-8.因此:

當從csv讀取資料(data)到資料庫的時候,需要先把GB2312轉換為unicode編碼,然後再把unicode編碼轉換為utf-8編碼:data.decode('GB2312').encode('utf-8')

當從資料庫讀取資料(data)存到csv檔案的時候,需要先把utf-8編碼轉換為unicode編碼,然後再把unicode編碼轉換為GB2312編碼:data.decode('utf-8').encode('GB2312')
'''

# windows下使用GB2312
# linux下使用utf-8
csvFile = open("files/gdpList.csv",'wt',newline='',encoding="GB2312")
writer = csv.writer(csvFile)


# 另外注意pycharm中開啟csv檔案肯能感覺是亂的,其實就是以逗號分開的,沒有問題
try:
    for row in rows:
        csvRow = []
        for cell in row.findAll('td'):
            csvRow.append(cell.getText())
        writer.writerow(csvRow)
finally:
    csvFile.close()

實驗結果:

5.3 python連線mysql

這裡的連線和java都是一個道理,但是相比java要簡便很多。安裝pymysql庫後自動匯入就可以使用pymysql

'''
# 1、簡單實驗:

import pymysql

conn = pymysql.Connect("localhost","root","root","pythontes")

cur = conn.cursor()
cur.execute("select * from pages")


# fetchone():"""Fetch the next row"""
# fetchall():"""Fetch all the rows"""
for row in cur.fetchall():
    print(row)
    cur.close()
conn.close()


# 以上輸出為:
# ('a1', 'liuBei', '123')
# ('a2', 'guanYu', '123')
# ('a3', 'zhangSanFei', '123')
'''


# 2、網頁儲存到mysql
# 資料庫儲存網頁資訊
# 還是上次那個GDP排名資料
from urllib.request import urlopen
from bs4 import BeautifulSoup
import codecs
import pymysql

html = urlopen("https://baike.baidu.com/item/%E5%9B%BD%E5%86%85%E7%94%9F%E4%BA%A7%E6%80%BB%E5%80%BC/31864?fromtitle=GDP&fromid=41201")
bsObj = BeautifulSoup(html,"html5lib")

# print(bsObj)

table = bsObj.findAll("table",{"class":"table-view log-set-param"})[1]
rows = table.findAll("tr")

# 輸出到資料庫mysql
conn = pymysql.connect("localhost","root","root","pythontes")

# 錯誤記錄1:UnicodeEncodeError: 'latin-1' codec can't encode character
# 原因:windows系統編碼不同
# 參考連結:https://stackoverflow.com/questions/3942888/unicodeencodeerror-latin-1-codec-cant-encode-character
conn.set_charset("utf8")
cur = conn.cursor()

try:
    tag = True
    for row in rows:
        # 這裡需要跳過第一行說明部分
        if tag:
            tag = False
            continue

        csvRow = []
        for cell in row.findAll('td'):
            csvRow.append(cell.getText())
        print(csvRow)

        '''
        錯誤記錄2:
        由於爬取的網頁都是字串型別,但是資料庫不支援格式轉換寫入,
        即(%d,int(csvRow[0]))轉換無效,所以只能將所有的型別儲存為字串型別
        '''

        # 一種寫法
        # cur.execute("insert into gdpList values(%s,%s,%s,%s,%s,%s)",(csvRow[0],csvRow[1],csvRow[2],csvRow[3],csvRow[4],csvRow[5]))

        # 另一種寫法
        sql = "insert into gdpList values(%s,%s,%s,%s,%s,%s)"
        cur.execute(sql,csvRow)

        # 提交
        cur.connection.commit()
finally:
    cur.close()
    conn.close()

資料庫實現網路圖的爬取(深度爬取)
簡要談談資料庫的優化:

  • id和主鍵:首先最好每一個表都要有一個id欄位,對資料查詢都是有幫助的;關鍵字一般建立為單一不變的值,最能代表此行資料的欄位
  • 索引:索引用該改善資料的查詢優化,根據不同的資料形式設定對應的索引,索引相當於字典,比如我們可以對建立一個欄位的前10個字元作為索引:1、ALTER TABLE table_name ADD INDEX index_name (column_list(10)) ;2、CREATE INDEX index_name ON table_name (column_list(10))。
  • 分表來節省空間和時間:單個數據表可能存在大量的重複資料,比如統計一個鎮的所有居民資訊,對於村這個欄位,可能很多人都是在一個村,所以存在大量的重複。因此分為:鎮-村表,和村-居民表,要節省不少空間,同時查詢速度也快

以下為爬取深度網路,並將網路連結對應資訊儲存;使用了兩個資料庫表

# 建資料庫表
CREATE TABLE `pages` (
`id` INT NOT NULL AUTO_INCREMENT,
`url` VARCHAR(255) NOT NULL,
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)

CREATE TABLE `links` (
`id` INT NOT NULL AUTO_INCREMENT,
`fromPageId` INT NOT NULL,
`toPageId` INT NOT NULL,
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)
""" 
@author: zoutai
@file: linkToLink.py 
@time: 2018/01/24 
@description: 爬取一個深度為4的網路,儲存網路之間的連線關係(兩兩對應)
"""

import re
from urllib.request import urlopen

import pymysql
from bs4 import BeautifulSoup

conn = pymysql.connect("localhost", "root", "root", "pythontes")
conn.set_charset("utf8")
cur = conn.cursor()

# 定義一個set,對page進行去重
pages = set()


def insertLink(fromPageId, toPageId):
    cur.execute("select * from links where fromPageId = %s and toPageId = %s", (int(fromPageId), int(toPageId)))
    if cur.rowcount == 0:
        cur.execute("insert into links (fromPageId,toPageId) values (%s,%s)", (int(fromPageId), int(toPageId)))
        conn.commit()


# 返回當前url儲存的id
def insertPage(pageUrl):
    cur.execute("select * from pages where url = %s", (pageUrl))
    if cur.rowcount == 0:
        cur.execute("insert into pages (url) values (%s)", pageUrl)
        conn.commit()
        return cur.lastrowid
    else:
        return cur.fetchone()[0]


def getLinks(pageUrl, recursionLevel):
    global pages
    if recursionLevel < 0:
        return
    pageId = insertPage(pageUrl)
    html = urlopen("https://en.wikipedia.org" + pageUrl)
    bsObj = BeautifulSoup(html, "html5lib")
    for link in bsObj.findAll("a", href=re.compile("^(/wiki/)((?!:).)*$")):
        insertLink(pageId, insertPage(link.attrs["href"]))
        if link.attrs['href'] not in pages:
            newPage = link.attrs['href']
            pages.add(newPage)
            getLinks(newPage, recursionLevel - 1)


getLinks("/wiki/Kevin_Bacon", 2)

5.4 傳送Email資料,使用163的SMTP郵件伺服器

“””
@author: zoutai
@file: sendEmail.py
@time: 2018/01/24
@description: 郵件傳送
“”“

完全參考:連結

from smtplib import SMTP
from email.mime.text import MIMEText
from email.header import Header

def send_email(SMTP_host, from_addr, password, to_addrs, subject, content):
    email_client = SMTP(SMTP_host)
    email_client.login(from_addr, password)
    # create msg
    msg = MIMEText(content,'plain','utf-8')
    msg['Subject'] = Header(subject, 'utf-8')#subject
    msg['From'] = 'soundslow<[email protected]>'
    msg['To'] = "[email protected]"
    email_client.sendmail(from_addr, to_addrs, msg.as_string())

    email_client.quit()

if __name__ == "__main__":
    send_email("smtp.163.com","[email protected]","xxx","[email protected]","來自163的郵件","今晚的衛星有點亮")

相關推薦

python網路資料採集-5儲存資料

5.1 媒體檔案簡述 網路上的資源很多,有圖片,視訊,常規檔案rar\zip等,由於網路爬去的資料量大,如果直接儲存,相對只儲存對應的連結字串,有很多缺陷:1、由於下載,導致爬取速度慢;2、消耗儲存空間;3、而且還要實現檔案下載的方法,繁瑣;優點:1、防

Python基礎教程之5 條件, 循環和其它語句

like eba cti python基礎 word 沒有 positive while循環 pytho Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32

Python--Redis實戰:資料安全與效能保障:7節:非事務型流水線

之前章節首次介紹multi和exec的時候討論過它們的”事務“性質:被multi和exec包裹的命令在執行時不會被其他客戶端打擾。而使用事務的其中一個好處就是底層的客戶端會通過使用流水線來提高事務執行的效能。本節將介紹如何在不使用事務的情況下,通過使用流水線來進一步提升命令的執行效能。 之前章節曾經介紹過一

《TCP/IP網路程式設計》5 筆記&程式碼&註釋

在第四章中的回聲迭代客戶端中,客戶端通過write()一次性將字串傳送過去,之後呼叫read()等待接受自己傳輸的字串,因此這個回聲迭代客戶端是不完美的。 解決方法就是提前確定接受資料的大小: //Linux while(1) { //......相同省略 int str_l

Python--Redis實戰:資料安全與效能保障:8節:關於效能方面的注意事項

習慣了關係資料庫的使用者在剛開始使用Redis的時候,通常會因為Redis帶來的上百倍的效能提升而感到欣喜若狂,卻沒有認識到Redis效能實際上還可以進一步的提高。雖然上一節介紹的非事務型流水線可以儘可能地減少應用程式和Redis之間的通訊往返次數,但是對於一個已

5 關係資料理論 練習

                 1.規範化理論是關係資料庫進行邏輯設計的理論依據,根據這個理論,關係資料庫中的關係必須滿足:每 一個屬性都是( )。      A.長度不變的      B.不可分解的      C.互相關聯的      D.互不相關的                            

Unix網路程式設計上卷--5

     在三路握手中,客戶接收到三路握手的第二個分節時,connect函式返回,而伺服器要直到接收到三路握手的第三個分節accept函式才返回(即connect返回之後再過一半RTT返回)。 使用命令: %:ps -A -o pid,ppid,tty,stat,args,

資料基礎---《利用Python進行資料分析·2版》5 pandas入門

之前自己對於numpy和pandas是要用的時候東學一點西一點,直到看到《利用Python進行資料分析·第2版》,覺得只看這一篇就夠了。非常感謝原博主的翻譯和分享。 pandas是本書後續內容的首選庫。它含有使資料清洗和分析工作變得更快更簡單的資料結構和操作工具。pandas經常和其它工

【程式碼】5 資料儲存

5.1.2 json import json info = { 'name': '王偉', 'gender': '難', 'birthday': '1992-10-08' } with open('512.json','w', encod

Visual C++網路程式設計經典案例詳解 5 網頁瀏覽器 HTTP響應 實體資料 類獲取響應訊息的響應碼

結構體的用法很簡單。 例如,利用該類獲取響應訊息的響應碼 程式碼如下 ... message msg; //結構體物件 CString str; //存放響應碼 msg.messagehead=&recvdata; //recvdata 為接收到的響應訊息

Visual C++網路程式設計經典案例詳解 5 網頁瀏覽器 HTTP響應 實體資料 自定義一個簡單的訊息體結構

總之,伺服器返回的響應訊息類似於C++語言中的結構體 訊息頭和訊息體就是這個結構體裡面的元素。 使用者在使用HTTP程式設計時, 可以根據需要自定義一個結構體儲存該訊息資料。 例如,自定義一個簡單的訊息結構體 typedef struct { char *messagehead; /

Visual C++網路程式設計經典案例詳解 5 網頁瀏覽器 HTTP響應 實體資料 伺服器的訊息響應格式

在伺服器的響應訊息中包括了訊息頭和訊息體兩部分。 其中訊息體中包含的實體資料。 並且在訊息頭和實體資料之間使用一個空白行進行分隔。 例如 客戶端向伺服器請求一個頁面GET.html 伺服器的響應訊息格式如下 HTTP/1.1 200 OK//訊息頭。 Date: Mon,21 Nov

Python資料科學手冊-5機器學習

文章目錄 機器學習的分類 Scikit-Learn Scikit-Learn的資料表示 Scikit-Learn的評估器API 應用:手寫數字探索 超引數與模型驗證

利用Python進行資料分析——8繪圖及視覺化——學習筆記Python3 5.0.0

matplotlib API 入門 matplotlib API 函式(如plot和close)都位於matplotlib.pyplot模組中,通常的引入方式如下: import matplotlib.pyplot as plt Figure和Subplot matplot

《Think Python5學習筆記

ssi toc turn nal rec source CA 執行c -c 備忘:parameter 指的是形參,argument 指的是實參。 [TOC] 5.1 整除和取模(Floor division and modulus) 在 Python 3 中,/ 符號對

Python從小白到大牛》5 Python編碼規範

spec 數列 微信 types optional 的人 斷開 包含 其他人 俗話說:“沒有規矩不成方圓”。編程工作往往都是一個團隊協同進行,因而一致的編碼規範非常有必要,這樣寫成的代碼便於團隊中的其他人員閱讀,也便於編寫者自己以後閱讀。 提示關於本書的Python編碼規

筆記 -《計算機網路:自頂向下方法》 5 鏈路層:鏈路、接入網和區域網(0)

第5章 鏈路層:鏈路、接入網和區域網(0)   ** “結構” 均為本章知識結構; ** “假設” 均為理想化,抽象的模型; ** “例項” 均為已經投入使用的模型; (結構1)   (假設1)同一子網內 傳遞網路層資料報的鏈路層工作流程 &nbs

利用Python進行資料分析之記錄 資料規整化:清理、轉換、合併、重塑

合併資料集: pandas物件中的資料可以通過一些內建的方式進行合併: pandas.merge可根據一個或多個鍵將不同DataFrame中的行連線起來。SQL或其它關係型資料庫的使用者對此應該會比較熟悉,因為它實現的就是資料庫的連線操作。 pandas.concat可以沿著一條軸將多個

易學筆記-5:數字/5.4 python表示式操作符/5.4.1 操作符

python表示式操作符 三元選擇表示式:x if y else z 邏輯或: x or y 邏輯與: x and y 邏輯非:not x 成員關係 x in y x not in y 物件實體測試

易學筆記-系統分析師考試-5 資料庫系統/5.2 資料模型/5.2.3 規範化理論

錯誤關係模式舉例:關係模式R(學生姓名,選修的課程名,任課老師,任課老師地址) 資料冗餘:不同學生的任課老師資料可能重複 修改異常:修改了一個一條記錄的任課老師地址後,其它同一個老師的地址都要修改 插入異常:如果不知道學生姓名,那麼任課老師的資訊就無法插入資料庫