[B站視頻]Python爬蟲技術5天速成

課程介紹

B站課程鏈接
學習主題:Python爬蟲和數據可視化
學習內容:
(1)Python語言的基礎知識
(2)網絡爬蟲的技術實現
(3)數據可視化的技術應用(框架、組件等)
學習目標: 瞭解網絡爬蟲和數據可視化的技術原理與流程

1.Python基礎概述

Python誕生和發展
1991年,第一個Python編譯器(同時也是解釋器)(可以解析你的指令了)誕生,它用C語言實現,並能調用C庫。
2000年,Python2.0發佈。
2008年,Python3發佈。
基本介紹
Python是一門解釋型(JAVA,C#不一樣,需要先編譯成一箇中間狀態,再運行中間狀態文件,生成一個運行文件,而Python是寫完之後直接可以執行的解釋型語言)、面向對象(和JAVA一樣,具有封裝,繼承,多態的功能)的高級編程語言。
Python是開源免費的、支持交互式、可跨平臺移植的腳本語言。
Python的設計混合了傳統語言的軟件工程的特點和腳本語言的易用性,具有如下特性
1.開源、易於維護
2.可移植
3.易於使用、簡單優雅
4.廣泛的標準庫、功能強大
5.可擴展、可嵌入
6. …
Python存在的缺點:
1.運行速度慢:Python是解釋型語言,邊執行需要邊翻譯成機器碼,耗時,而C語言則在運行前直接編譯成CPU能執行的機器碼。但大量應用程序不需要那麼快的運行速度,因爲用戶根本感覺不出來
2.代碼不能加密:解釋型語言發佈程序就是發佈源代碼,而C語言只需要把編譯後的機器碼發佈出去,從機器碼反推出C代碼是不可能的。
Python典型應用
在這裏插入圖片描述
在這裏插入圖片描述
1.3 第一個Python程序
在命令行/終端輸入python或者python3進入python環境
退出python環境的方法:
1.用exit()
2.按Ctrl+Z(遇到死循環也可以用這種方法退出)

如何在命令行/終端直接執行python文件:
在命令行/終端直接輸入

python <py文件名.py>

python3 <py文件名.py>

1.5 變量及類型
賦值(比如a=‘ABC’)時,Python解釋器幹了兩件事:
1.在內存中創建一個‘ABC’的字符串
2.在內存中創建一個名爲a的變量,並把它指向‘ABC’

1.6 標識符和關鍵字
什麼是關鍵字
是python的一些具有特殊功能的標誌符,這就是所謂的關鍵字,他們是python已經使用的,所以不允許開發者自己定義和關鍵字相同的名字的標誌符
查看關鍵字代碼:

import keyword
keyword.kwlist

返回結果是:

and	as 	assert	break	class	continue	def	del
elif	else	except	exec	finally	for	from	global
if	in	import	is	lambda	not	or	pass
print	raise	return	try	while	with	yield

1.7 輸出
格式化輸出:

age=18
print('這是我的年紀:%d歲'%age)

%d,格式化的是有符號十進制整數,%s也比較常用,格式化的是字符串

print('我的名字是%s,我的國籍是%s'%('小張','中國'))

補充:可以通過sep=’.'表示通過.將前面的字符串連接起來,如

print('www','baidu','com',sep='.')

返回結果是www.baidu.com
觀察以下代碼返回結果(功能已在代碼註釋中):

print('hello', end='')	  # 打印之後不空格直接跟下一個內容
print('world', end='\t')  # 打印之後自動tab佔位
print('python', end='\n') # 打印之後自動換行
print('end')			  # 不對end參數賦值,則默認end='\n'

結果:

helloworld	python
python
end

1.8 輸入

password = input('請輸入密碼:') 
print('您剛剛輸入的密碼是:',password)
print(type(password)) # 即使輸入的是數字,其實password存的還是字符串

注意: input輸入得到的是字符串,所以後續進行print輸出應該對應%s而不是%d
如果需要把字符串轉換成整型,就需要使用強制類型轉換int()

1.9 運算符和表達式
重點注意:假設a=10,b=21
:冪-返回x的y次冪,ab爲10的21次方
//:取整除-返回向下取接近除數的整數,9//2的結果是4,-9//2的結果是-5

補充: 隨機數模塊

import random # 引入隨機庫
x = random.randint(0, 2) # 隨機生成[0,2]的隨機數,包含0、1、2
print(x)
### import與from...import
在python中用import或者from...import來導入相應的模塊
將整個模塊(somemodule)導入,格式爲:import somemodule
從某個模塊中導入某個函數,格式爲:from somemodule import somefunction
從某個模塊中導入多個函數,格式爲:from somemodule import firstfunc, secondfunc, thirdfunc
將某個模塊中的全部函數導入,格式爲:from somemodule import \*

課堂作業1:IF語句實現石頭剪子布

import random
game_1 = input('請輸入:剪刀(0)、石頭(1)、布(2):') # 注意input裏面的引號不要忘記
if game_1 not in ['0','1','2']: # 因爲input賦予變量的類型是字符串,所以必須考慮not in['0','1','2']
    print('你輸入的不是0,1,2中的任何一個')
else:
    if game_1 == 0:
        print('你輸入爲:剪刀(0)')
    elif game_1 == 1:
        print('你輸入爲:石頭(1)')
    elif game_1 == 2:
        print('你輸入爲:布(2)')
    game_2 = random.randint(0,2)
    print('隨機生成數字爲:%d'%game_2)
    if (game_1==0 and game_2==2) or (game_1==1 and game_2==0) or (game_1==2 and game_2==1):
        print('啊,你贏了')
    elif game_1==game_2:
        print('哈哈,平局')
    else:
        print('哈哈,你輸了:)')

補充: python可以用isinstance()函數來判斷一個變量是否是某個類型,如

isinstance(a, int) # 判斷變量a是否是int類型,是則返回TRUE,否則返回FALSE

課堂作業2:用For和While循環打印九九乘法表

for i in range(1,10):
    for j in range(1,i+1):
        print('%d*%d=%d'%(i,j,i*j),end='\t')
    print()

補充: r取消轉義

print('hello\nchengdu') # 使用反斜槓,實現轉義字符的功能
print(r'hello\nchengdu') # 在字符串前面加r,表示直接顯示原始字符串,不進行轉義

字符串常見操作(只介紹部分常用到的)

bytes.decode(encoding='utf-8',error='strict')

1.python3中沒有decode方法,但我們可以使用bytes對象的decode()來解碼給定的bytes對象,這個bytes對象可以由str.encode()來編碼返回。

encode(encoding='utf-8',errors='strict')

2.以encoding指定的編碼格式編碼字符串,如果出錯默認報一個ValueError的異常,除非errors指定的是‘ignore’或者‘replace’

str.isalnum()

3.如果字符串至少有一個字符並且所有字符都是字母或數字則返回True,否則返回False

str.isalpha()

4.如果字符串至少有一個字符並且所有字符都是字母,則返回True,否則返回False

str.isdigit()

5.如果字符串只包含數字,則返回True,否則返回False

str.isnumeric()

6.如果字符串只包含數字字符,則返回True,否則返回False(注意和isdigit()是有區別的,可以搜索相關博客來了解)

str.join(seq)

7.以指定字符串seq爲分隔符,將str對象的所有的元素(的字符串表示)合併成一個新的字符串

len(str)

8.返回字符串長度

str.lstrip([chars])

9.截掉字符串左邊的指定字符,chars爲指定截取的字符,默認爲空格

str.rstrip([chars])

10.刪除 string 字符串末尾的指定字符,chars爲指定截取的字符,默認爲空格

str.split(str="", num=string.count(str))

11.以str爲分隔符截取字符串,如果num有指定值,則僅截取num+1個字符串

3.2列表

在這裏插入圖片描述
補充:

list.index(obj,start,stop)

從list中的start開始到stop結束(不包括stop)去查找obj,查到則返回下標,查不到則報錯

list.count(obj)

在list中統計obj對象有多少個,並返回

課堂作業3

products = [['iphone',6888],['MacPro',14800],['小米6',2499],['Coffee',31],['Book',60],['Nike',699]]
print('------ 商品列表 ------')
for i in range(len(products)):
    print('%d  %-5s\t%d'%(i,products[i][0],products[i][1])) # -5表示左對齊,且對應的字符串固定佔用5個字符位

store = []
while True:
    buy = input('請問您需要購買什麼?請輸入產品編號0-5(若完成購買則輸入\'q\'退出):')
    if buy == 'q':
        break
    else:
        store.append(int(buy)) # 因爲input得到的是字符串,所以需要int強制類型轉換

print('------ 購買的商品列表 ------')
total  = 0
store.sort() # 排序,爲了讓下面的列表好看一些
for index in store:
    value = products[index][1]
    print('%d  %-5s\t%d'%(index, products[index][0], value))
    total += value

print('購買的商品總數爲:',len(store))
print('購買的商品總價爲:',total)

3.3元組

Tuple(元組)
元組的元素不可變,但可以包含可變對象,如list。
注意:定義一個只有1個元素的tuple,必須加逗號,如果不加逗號,則得到的並不是一個元組,而是本身小括號裏面元素的類型。

3.4字典

字典的訪問方式:

info = {'name': '吳彥祖','age':18}
print(info['name']) # 普通的直接訪問
print(info['gender']) # 普通的直接訪問,沒找到會會報錯
print(info.get('gender')) # 使用get方法,沒有找到對應的鍵,默認返回None
print(info.get('gender','m')) # 可設置默認值,找到了則返回正確值,沒找到則返回默認值

字典的刪:

info = {'name': '吳彥祖','age':18}
print('刪除前:%s'%info['name'])
del info['name'] # 刪除整個鍵值對
print('刪除後:%s'%info['name'])

print('刪除前:%s'%info)
del info # 刪除整個字典變量
print('刪除後:%s'%info)

# [clear]
print('清空前:%s'%info)
info.clear() # 清空得到空的字典
print('清空後:%s'%info)

補充:通過枚舉函數enumerate()爲對象添加下標

mylist = ['a','b','c','d']
for i,x in enumerate(mylist): # enumerate讓我們能同時獲取下標和內容
    print(i,x)

3.5集合

3.6總結:

在這裏插入圖片描述

4.函數

補充:在函數內寫global a是申明當前使用的是全局變量

5.文件操作

常用訪問模式:
r:以只讀方式打開文件。文件的指針將會放在文件的開頭。這是默認模式。
w:打開一個文件只用於寫入。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
rb:以二進制格式打開一個文件用於只讀。文件指針將會放在文件的開頭。這是默認格式。
wb:以二進制格式打開一個文件只用於寫入。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
文件讀取的三種方式

# read方法,讀取指定的字符,開始時定位在文件頭部,每執行一次向後移動指定的字符數
f = open('test.txt','r')
content = f.read(5) # 讀5個字符出來
print(content)
content = f.read(5)
print(content)
f.close()

read(n):讀第n個字符
readlines():把整個文檔都讀入了一個list中,每行對應list的一個元素
readline():一次讀一行

5.3文件的相關操作

有時候需要對文件進行重命名、刪除等曹走,python中的os有這些功能

5.3.1文件重命名

import os
os.rename(需要修改的文件名, 新的文件名)

5.3.2刪除文件

import os
os.remove(待刪除的文件)

5.3.3創建文件夾

import os
os.mkdir(文件夾名)

5.3.4獲取當前目錄

import os
os.getcwd()

5.3.5改變默認目錄

import os
os.chdir('../')

5.3.6獲取目錄列表

import os
os.listdir('./')

5.3.7刪除文件夾

import os
os.rmdir(文件夾名)

6.錯誤與異常

捕獲異常:

# 捕獲異常
try:
    print('--------test------1---')
    f = open('123.txt','r')
    print('--------test------2---')
except IOError: # 文件沒找到屬於IO異常-IOError(輸入輸出異常)
    pass        # 捕獲異常後,執行的代碼

異常類型想要被捕獲,必須要一致

try:
    print(num)
except NameError:  # 異常類型想要被捕獲,必須要一致
    print('產生錯誤了')

將可能產生的所有異常類型,都放到括號中

try:
    print('--------test------1---')
    f = open('test1.txt','r')
    print('--------test------2---')
    print(num)
except (NameError, IOError):  # 將可能產生的所有異常類型,都放到括號中
    print('產生錯誤了')

獲取錯誤描述(except Error類型 as result)

try:
    print('--------test------1---')
    f = open('123.txt','r')
    print('--------test------2---')
    print(num)
except (NameError, IOError) as result:  
    print('產生錯誤了')
    print(result)

捕獲所有異常(except Exception)

try:
    print('--------test------1---')
    f = open('123.txt','r')
    print('--------test------2---')
    print(num)
except Exception as result: # Exception可以承接任何異常
    print('產生錯誤了')
    print(result)

try…finally 和嵌套

import time
try:
    f = open('test1.txt','r') # 文件打開

    try :
        while True:
            content = f.readline()
            if len(content) == 0:
                break
            time.sleep(2)
            print(content)
    finally:            # 如果能進入第二層try,說明文件打開了,文件打開。第二層try無論報任何錯,都需要colse文件
        f.close()
        print('文件關閉')

except Exception as result:
    print('發生異常')
    print(result)

課堂作業4

# 通過python新建一個gushi.txt,選擇一首古詩寫入文件中
def write_file(file_name,gushi):
    f = open(file_name,'w') # 在這裏我默認寫文件不會出錯
    try:
        f.writelines(gushi)
    except Exception as result:
        print('文件寫入出錯了,原因是:',result)
    finally:
        f.close()


write_file('gushi.txt',['牀前明月光\n','疑是地上霜\n','舉頭望明月\n','低頭思故鄉'])


# 另外寫一個函數,讀取指定文件gushi.txt,將內容複製到copy.txt中,並在控制檯輸出'複製完畢'。
def read_file(file_name):
    try:
        f = open(file_name,'r')
        try:
            content = f.readlines()
        finally:
            f.close()
        return content
    except Exception as result:
        print('讀取文件錯誤,原因是:',result)


content = read_file('gushi.txt')
write_file('copy.txt', content)
print('複製完畢')

Python爬蟲(任務驅動)

1.任務介紹

需求分析
爬取豆瓣電影Top250的基本信息,包括電影的名稱、豆瓣評分、評價數、電影概況、電影鏈接等。要爬取的網站

2.爬蟲初識

什麼是爬蟲:網絡爬蟲,是一種按照一定規則自動抓取互聯網信息的程序或者腳本。由於互聯網數據的多樣性和資源的有限性,根據用戶需求定向抓取相關網頁並分析已成爲如今主流的爬取策略。
爬蟲的本質是什麼模擬瀏覽器打開網頁,獲取網頁中我們想要的那部分數據。

3.基本流程

準備工作:通過瀏覽器查看分析目標網頁,學習編程基礎規範。
獲取數據:通過HTTP庫向目標站點發起請求,請求可以包含額外的header等信息,如果服務器能正常響應,會得到一個Response,便是所要獲取的頁面內容。
解析內容:得到的內容可能是HTML、json等格式,可以用頁面解析庫正則表達式等進行解析。
保存數據:保存形式多樣,可以存爲文本,也可以保存到數據庫,或者保存特定格式的文件。

3.1準備工作

URL分析:
第一頁對應的URL爲:https://movie.douban.com/top250
第二頁對應的URL爲:https://movie.douban.com/top250?start=25&filter=
其中的&filter=沒有取值,所以可以去掉並不會影響,有用的主要是start=25,他表示顯示下標從25開始的電影(下標從0開始,所以實際上對應的標籤是26)

總結:
頁面包括250條電影數據,分10頁,每頁25條
每頁的URL的不同之處:最後的數值=(頁數-1)*25

3.1.1 分析頁面

藉助瀏覽器開發者工具來分析網頁,在Elements下找到需要的數據位置

3.1.2 編碼規範

一般Python程序第一行需要加入# -*- coding: utf-8 -*- 或者 # coding=utf-8,這樣可以在代碼中包含中文
Pytho文件中可以加入main函數用於測試程序:if __name__ == '__main__':

3.1.3 引入模塊

模塊(module):用來從邏輯上組織python代碼(變量、函數、類),本質就是py文件,提高代碼的可維護性。Python使用import來導入模塊,如import sys
模塊module一般情況下是以.py來後綴的文件。

3.2獲取數據

補充urllib

get請求:

import urllib.request

#獲取一個get請求
response = urllib.request.urlopen('http://www.baidu.com') # 等同於request.get(),後者更簡單
print(response.read().decode('utf-8'))  # 一行顯示的話需要打開soft-wrap,參考:https://blog.csdn.net/qq_38157825/article/details/89331743

當用urllib去獲取一個網頁的時候,建議對返回來的內容進行一個解碼
補充:一個提供http請求和響應服務的測試網站:http://httpbin.org/
post請求:

#獲取一個post請求
import urllib.parse
data  = bytes(urllib.parse.urlencode({'hello':'world'}),encoding='utf-8')
response = urllib.request.urlopen('http://httpbin.org/post',data)
print(response.read().decode('utf-8'))

超時處理:

# 超時處理,try_except
try:
    response = urllib.request.urlopen('http://httpbin.org/get',timeout=0.01)
    print(response.read().decode('utf-8'))
except urllib.error.URLError as e:
    print('time out!')

獲取頭部信息:

response = urllib.request.urlopen('http://www.baidu.com')
# print(response.status) # response包含狀態碼
print(response.getheaders()) # response包含頭部信息
print(response.getheader('Server')) # 拿具體一個屬性就是getheader

如果被發現是爬蟲,可能會返回418Error(有一個梗-我是茶壺),所以需要僞裝成瀏覽器
模擬瀏覽器:

url  = 'http://httpbin.org/post'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 QQBrowser/4.4.119.400'
}  # 鍵值對可以增加更多
data = bytes(urllib.parse.urlencode({'name':'eric'}), encoding='utf-8')
req = urllib.request.Request(url=url, data=data, headers=headers,method='POST') # 構建請求對象
response = urllib.request.urlopen(req) # 發起請求,返回響應
print(response.read().decode('utf-8'))

訪問豆瓣:

url = 'https://www.douban.com'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 QQBrowser/4.4.119.400'
} # 僞裝瀏覽器
req = urllib.request.Request(url = url,headers=headers)
response = urllib.request.urlopen(req)
print(response.read().decode('utf-8'))

補充:有時User-Agent只指定爲Mozilla/5.0就可以僞裝成功了
補充contrl+shift+J可以快速合併到一行

補充BeautifulSoup

補充資料:
beautifulsoup4簡介
Beautiful Soup 4.2.0 文檔

BeautifulSoup4將複雜HTML文檔轉換成一個複雜的樹形結構,每個節點都是Python對象,所有對象可以歸納爲4種:

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment
from bs4 import BeautifulSoup

file = open('./baidu.html','rb')
html = file.read()
bs = BeautifulSoup(html,'html.parser')

#1.Tag: 標籤
print(bs.title) # 返回:<title>百度一下,你就知道 </title>
# print(bs.a)
# print(bs.head)
print(type(bs.head)) # 類型爲bs4.element.Tag

#2.NavigableString: 標籤裏的內容(字符串)
print(bs.title.string) # 返回:百度一下,你就知道
print(type(bs.title.string)) # 類型爲bs4.element.NavigableString
print(bs.a.attrs) # 拿到一個標籤裏面的所有屬性

#3.BeautifulSoup:  表示整個文檔
print(type(bs))
print(bs.name)
print(bs.attrs)
print(bs)

#4.comment: 是一個特殊的NavigableString,但輸出的內容不包含註釋符號
print(bs.a.string)
print(type(bs.a.string))

文檔的遍歷:

# 文檔的遍歷
print(bs.head.contents) # 返回一個list
print(bs.head.contents[1])
# 更多內容,搜索網絡相關

文檔的搜索:

# (1)find_all()--最常用
# 字符串過濾:會查找與字符串完全匹配的內容
t_list = bs.find_all('a') # 查詢所有的a標籤
# print(t_list)

import re
# 正則表達式:使用search()方法來匹配內容
t_list = bs.find_all(re.compile('a')) # 查詢其中帶有字符a的所有標籤
# print(t_list)

# 方法:傳入一個函數(方法),根據函數的要求來搜索(瞭解)
def name_is_exists(tag): #搜索並返回標籤裏面含有name屬性的
    return tag.has_attr('name')

t_list = bs.find_all(name_is_exists)
# print(t_list)


# 2.kwargs      參數
t_list = bs.find_all(id='head')
t_list = bs.find_all(class_=True) # class是python的保留字,所以bs4作者加了下劃線(規定)
t_list = bs.find_all(href='http://news.baidu.com') # 精確查找
# for item in t_list:
    # print(item)

# 3.text參數
t_list = bs.find_all(text='hao123')
t_list = bs.find_all(text=['hao123','地圖','貼吧','我'])
t_list = bs.find_all(text=re.compile('\d')) # 應用正則表達式來查找包含特定文本的內容(標籤裏的字符串)
# for item in t_list:
    # print(item)

# 4.limit參數
t_list  = bs.find_all('a',limit=3) # 限制獲取的個數
# for item in t_list:
    # print(item)


# 5.css選擇器
t_list = bs.select('title') # 通過標籤來查找
t_list = bs.select('.mnav') # 通過類名來查找,.表示查找的是類,跟的mnav是類名
t_list = bs.select('#u1')   # 通過id來查找,#表示查找的是id,跟的u1是id名
t_list = bs.select("a[class='bri']") # 通過屬性來查找
t_list = bs.select('head>title') # 通過子標籤來查找
# for item in t_list:
#     print(item)


t_list = bs.select('.mnav ~ .bri') # 跟.mnav是兄弟的一個標籤,且該兄弟標籤值是.bri
print(t_list[0].get_text())

補充:re模塊

正則表達式的常用操作符號(1)
在這裏插入圖片描述在這裏插入圖片描述
補充史上最全正則表達式大全鏈接
補充: Re庫主要功能函數
在這裏插入圖片描述
正則表達式的匹配模式控制在這裏插入圖片描述

3.3解析內容

解析頁面內容:
1.使用BeautifulSoup定位特定的標籤位置
2.使用正則表達式找到具體的內容

對爬取的html進行解析
1.使用BeautifulSoup定位特定的標籤位置(類名爲’item’的div標籤):

soup = BeautifulSoup(html, 'html.parser')    #使用html.parser解析器
for item in soup.find_all('div', class_="item"): # 查找符合要求的字符串,形成列表

注意: class是python關鍵字,所以必須寫class_

2.使用正則表達式找到具體的內容:(以解析’影片詳情鏈接’舉例)
定義查找’影片詳情’的正則表達式:

# 影片詳情鏈接的規則
findLink = re.compile(r'<a href="(.*?)">')

利用re庫來通過正則表達式查找指定的字符串:

link = re.findall(findLink, item)[0]     #re庫用來通過正則表達式查找指定的字符串

補充python 爬蟲爬取內容時, \xa0 、 \u3000 的含義與處理方法

str = str.replace(u'\xa0', u'') #去掉'\xa0'

3.4保存數據

3.4.1Excel表存儲

利用python庫xlwt將抽取的數據datalist寫入Excel表格

# 保存數據
import xlwt # 進行excel操作
def saveData(datalist, savepath):
    print('save....')
    book = xlwt.Workbook(encoding='utf-8') # 創建工作簿
    sheet = book.add_sheet('豆瓣電影Top250')# 添加工作表
    col = ('電影詳情鏈接','圖片鏈接','影片中文名','影片外文名','評分','評價數','概況','相關信息')							   # 列名設置
    for i in range(0,8):
        sheet.write(0,i,col[i]) 		   # 寫入列名
    for i in range(0,250):
        print("第%d條"%(i+1))
        data = datalist[i]
        for j in range(0,8):
            sheet.write(i+1,j,data[j])    # 往工作表單元格寫入數據
    book.save(savepath)  			  	  # 保存表格

補充: SQLite

連接數據庫:

import sqlite3
# 1.連接數據庫
conn = sqlite3.connect('test.db')  # 打開或創建數據庫文件
print('成功打開數據庫')

創建數據表:

# 2.創建數據表
conn = sqlite3.connect('test.db')  # 打開或創建數據庫文件
print('成功打開數據庫')
c = conn.cursor() # 獲取遊標

sql = '''
    create table company
        (id int primary key not null,
        name text not null,
        age int not null,
        address char(50),
        salary real);
'''

c.execute(sql)    # 執行sql語句
conn.commit()        # 提交數據庫操作
conn.close()      # 關閉數據庫連接
print('成功建表')

插入數據:

conn = sqlite3.connect('test.db')  # 打開或創建數據庫文件
print('成功打開數據庫')
c = conn.cursor() # 獲取遊標

sql1 = '''
    insert into company (id, name, age, address, salary)
    values (1,'張三',32,'成都',8000);
'''
sql2 = '''
    insert into company (id, name, age, address, salary)
    values (2,'李四',30,'重慶',15000);
'''

c.execute(sql1)    # 執行sql語句
c.execute(sql2)    # 執行sql語句
conn.commit()        # 提交數據庫操作
conn.close()      # 關閉數據庫連接
print('插入數據完畢')

查詢數據:

conn = sqlite3.connect('test.db')  # 打開或創建數據庫文件
print('成功打開數據庫')
c = conn.cursor() # 獲取遊標

sql = '''
    select id, name, address, salary from company


'''

cusor = c.execute(sql)    # 執行sql語句

for row in cusor:
    print('id = ', row[0])
    print('name = ', row[1])
    print('address = ', row[2])
    print('salary = ', row[3],'\n')


# conn.commit()           # 查詢不需要提交
conn.close()      # 關閉數據庫連接
print('查詢完畢')

最終爬蟲代碼

#-*- coding = utf-8 -*-
#@Time ; 15/06/2020 10:48
#@Author : 張億鋒
#@File : spider.py
#@Software : PyCharm


from bs4 import BeautifulSoup  # 網頁解析,獲取數據
import re   # 正則表達式,進行文字匹配
import urllib.request, urllib.error # 制定URL,獲取網頁數據
import xlwt # 進行excel操作
import sqlite3 # 進行SQLite數據庫操作


def main():
    baseurl = 'https://movie.douban.com/top250?start='
    #1.爬取網頁
    datalist = getData(baseurl)

    #2.保存數據到excel表格中
    # savepath = '豆瓣電影Top250.xls'
    # saveData(datalist,savepath)

    #2.保存數據到數據庫中
    dbpath = 'movie.db'
    saveData2DB(datalist, dbpath)


#影片詳情鏈接的規則
findLink = re.compile(r'<a href="(.*?)">')     #創建正則表達式對象,表示規則(字符串的模式),這裏?是最小匹配,即非貪婪匹配,
                                               #括號表示一個組,等會findall返回的就是這個組的匹配上的字符串的這個組的內容
# findmagSrc = re.compile(r'<img.*src="(.*?)".*>')
#影片圖片的鏈接
findmagSrc = re.compile(r'<img.*src="(.*?)"',re.S) #re.S模式:使.匹配包括換行在內的所有字符
#影片片名
findTitle = re.compile(r'<span class="title">(.*)</span>')
#影片評分
findRating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>')
#找到評價人數
findJudge = re.compile('<span>(\d*)人評價</span>')
#找到概況
findInq = re.compile(r'<span class="inq">(.*)</span>')
#找到影片的相關內容
findBd = re.compile(r'<p class="">(.*?)</p>',re.S) # 必須要有?, 否則匹配會去匹配直到第二個</p>


# 爬取網頁
def getData(baseurl):
    datalist = []
    for i in range(0,10): # 調用獲取頁面信息的函數10次
        url = baseurl + str(i*25)
        html = askURL(url) # 保存獲取到的網頁源碼


        # 2.逐一解析數據
        soup = BeautifulSoup(html, 'html.parser')    #使用html.parser解析器
        for item in soup.find_all('div', class_="item"): # 查找符合要求的字符串,形成列表
            # print(item)   # 測試: 查看電影item全部信息
            data = []       # 保存一部電影的所有信息
            item = str(item)

            #影片詳情的鏈接
            link = re.findall(findLink, item)[0]     #re庫用來通過正則表達式查找指定的字符串
            data.append(link)                        #添加鏈接

            imgSrc = re.findall(findmagSrc, item)[0]
            data.append(imgSrc)                      #添加圖片

            titles = re.findall(findTitle, item)     #片名可能只有一箇中文名,沒有外文名
            if (len(titles) == 2):
                ctitle = titles[0]
                data.append(ctitle)                  #添加中文名
                otitle = titles[1].replace('/','')   #去掉無關的符號
                otitle = otitle.replace(u'\xa0', u'')  # 去掉無關的符號
                data.append(otitle)                  #添加外文名
            else:
                data.append(titles[0])
                data.append(' ')                     #留空

            rating = re.findall(findRating,item)[0]
            data.append(rating)                      #添加評分

            judgeNum = re.findall(findJudge,item)[0]
            data.append(judgeNum)                    #添加評價人數

            inq = re.findall(findInq,item)
            if len(inq) != 0:
                inq = inq[0].replace('。','')        #去掉句號
                data.append(inq)  # 添加概述
            else:
                data.append('')

            bd = re.findall(findBd, item)[0]
            # \s是指空白,包括空bai格、換行、tab縮進等所有的空白,單獨的問號表示匹配前面的子表達式0次或者1次
            bd = re.sub('<br(\s+)?/>(\s+)?',' ',bd)  #去掉<br/>
            bd = re.sub('/',' ',bd)                  #替換/
            bd = bd.replace(u'\xa0', u'')
            data.append(bd.strip())                  #去掉前後的空格

            datalist.append(data)                    #把處理好的一部電影信息放入datalist

    # print(datalist) # 測試
    return datalist


# 得到指定一個URL的網頁內容
def askURL(url):
    head = {} # 模擬瀏覽器頭部信息,向豆瓣服務器發送消息
    # 用戶代理,表示告訴豆瓣服務器,我們是什麼類型的機器,瀏覽器(本質上是告訴劉籃球,我們可以接受什麼水平的文件內容)
    head['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 QQBrowser/4.4.119.400' # contrl+shift+J可以快速合併到一行
    request = urllib.request.Request(url,headers=head)
    html = ''
    try:
        response = urllib.request.urlopen(request)
        html = response.read().decode('utf-8')
        # print(html)
    except urllib.error.URLError as e:
        if hasattr(e,'code'):
            print(e.code) # e.code是錯誤代碼
        if hasattr(e,'reason'):
            print(e.reason) # e.reason是錯誤原因
    return html


# 保存數據
def saveData(datalist, savepath):
    print('save....')
    book = xlwt.Workbook(encoding='utf-8')
    sheet = book.add_sheet('豆瓣電影Top250')
    col = ('電影詳情鏈接','圖片鏈接','影片中文名','影片外文名','評分','評價數','概況','相關信息')
    for i in range(0,8):
        sheet.write(0,i,col[i]) # 列名
    for i in range(0,250):
        print("第%d條"%(i+1))
        data = datalist[i]
        for j in range(0,8):
            sheet.write(i+1,j,data[j])

    book.save(savepath)


def saveData2DB(datalist, dbpath):
    init_db(dbpath)
    conn = sqlite3.connect(dbpath)
    cur = conn.cursor()

    for data in datalist:
        for index in range(len(data)):
            if index == 4 or index == 5:
                continue
            data[index] = '"'+str(data[index])+'"' # score和rated不需要

        sql = '''
            insert into movie250 (
            info_link, pic_link, cname, ename, score, rated, introduction, info) values (%s)
            '''%",".join(data)
        print(sql)
        cur.execute(sql)
        conn.commit()
    cur.close()
    conn.close()




def init_db(dbpath):
    sql  = '''
        create table movie250
        (
        id integer primary key autoincrement,
        info_link text,
        pic_link text,
        cname varchar,
        ename varchar,
        score numeric,
        rated numeric,
        introduction text,
        info text
        )    
    '''     # 創建數據表
    conn = sqlite3.connect(dbpath)
    cursor = conn.cursor()
    cursor.execute(sql)
    conn.commit()
    conn.close()


if __name__ == '__main__':
    main()
    # init_db('movietest.db') # 測試
    print('爬取完畢')

數據可視化

1.Flask入門

1.1.1瞭解框架

Flask作爲Web框架,它的作用主要是爲了開發Web應用程序。那麼我們首先來了解下Web應用程序。Web應用程序(World Wide Web)誕生最初的目的,是爲了利用互聯網交流工作文檔。

1.2.1從Helloworld開始

框架:

from flask import Flask, render_template,request
app = Flask(__name__) # 初始化
# 路由解析,通過用戶訪問的路徑,匹配相應的函數
@app.route('/')
def hello_world():
    return 'Hello World!'
if __name__ == '__main__':
    app.run()

補充: debug模式務必開啓,開啓方法是:右上角
在這裏插入圖片描述
點擊Edit Configurations後,勾選FLASK_DEBUG
通過訪問路徑,獲取用戶的字符串參數:

# 通過訪問路徑,獲取用戶的字符串參數
@app.route('/index/<name>')
def welcome(name):
    return '你好,%s'%name

通過訪問路徑,獲取用戶的整型參數:

# 通過訪問路徑,獲取用戶的整型參數
@app.route('/index/<int:id>')
def welcome2(id):
    return '你好,%d號的會員'%id

補充: 此外,還可設置float類型。總之,通過填寫不同的參數(不同的訪問路徑),可以進入不同的路徑,到不同的網頁。
注意: 路由路徑不能重複,用戶只能通過唯一路徑來訪問特定的函數。(當然可以通過不同的函數進入之後再跳轉到其他函數,但這裏不做介紹)
返回利用render_template(jinjia2)渲染網頁:

# 返回給用戶渲染後的網頁文件
@app.route('/i')
def index2():
    return render_template('index.html')

動態-向網頁傳遞一個變量(利用render_template(jinjia2)進行渲染):

三種變量類型:
普通變量
列表類型變量
字典類型變量

1.傳遞普通變量:

@app.route('/i3')
def index3():
    time = datetime.date.today()    # 普通變量
    return render_template('index.html',var = time)

在網頁html中使用var變量的方式:

今天是{{ var }}, 歡迎光臨.

2.傳遞列表類型變量:

@app.route('/i3')
def index3():
    time = datetime.date.today()    # 普通變量
    name = ['小張','小王','小趙']     # 列表類型
    return render_template('index.html',var = time, list = name)

在網頁html中使用list變量的方式:

{% for data in list %}   <!--用大括號和百分號括起來的是控制結構,還有if,可自行了解-->
	<li> {{ data }} </li>
{% endfor %}

3.傳遞字典類型變量:

@app.route('/i3')
def index3():
    time = datetime.date.today()    # 普通變量
    name = ['小張','小王','小趙']     # 列表類型
    task = {'任務':'打掃衛生','時間':'3小時'} # 字典類型
    return render_template('index.html',var = time, list = name, task = task)

注意: 實際中常採用類似task=task的寫法,前面的表示傳入html的參數名,後面是在.py文件中的變量名
在網頁html中使用task變量的方式:(這裏結合了表格)

<table border="1">
	{% for key,value in task.items() %} 
		<tr>
			<td>{{ key }}</td>
            <td>{{ value }}</td>
       	</tr>
	{% endfor %}
</table>

表單提交

1.構建表單提交的頁面register.html:

{#<form action="http://localhost:5000/result" method="post">#}
<form action="{{ url_for('result')}}" method="post"> <!--不使用localhost,而是通過反向路由(根據路由函數名得到路由地址)-->
    <p>姓名: <input type="text" name="姓名"></p>
    <p>年齡: <input type="text" name="年齡"></p>
    <p>性別: <input type="text" name="性別"></p>
    <p>地址: <input type="text" name="地址"></p>
    <p><input type="submit" value="提交"></p>
</form>

注意: 不使用localhost,而是通過反向路由(根據路由函數名得到路由地址)
2.在app.py中添加register.html的訪問路徑和訪問路由:

# 表單提交
@app.route('/test/register')
def register():
    return render_template('test/register.html')

3.構建接受表單提交顯示的頁面result.html:

<table border="1">
	{% for key,value in result.items() %}
	    <tr>
	        <th>{{ key }}</th>
	        <td>{{ value }}</td>
	    </tr>
	{% endfor %}
</table>

4,構建接受表單的訪問路徑和訪問路由:

from flask import request
@app.route('/result', methods = ['POST','GET'])
def result():
    if request.method == 'POST':
        result = request.form # request可以獲取所有提交的表單信息,生成字典
        return render_template('test/result.html',result = result)

注意: 接受表單提交的路由,需要指定methodsPOST

豆瓣數據可視化

3.1首頁製作

3.2列表頁製作

2.Echarts應用

補充: GitHub上的js和css一般是不能單獨下載的。如需下載需要下載整個文件,比如下載.zip後綴的包,然後解壓,取出自己想要的js或css文件。
注意: 字符串經過jinjia渲染後可能會出現轉義字符的問題

for item in data:
    score.append(str(item[0])) # score是字符串列表
    num.append(item[1])
cur.close()
conn.close()
return render_template('score.html', score=score, num=num)

html中按通常方式調用score

data: {{ score }}

html源碼中出現轉義字符顯示問題:

data: [&#39;8.3&#39;, &#39;8.4&#39;, &#39;8.5&#39;, &#39;8.6&#39;, &#39;8.7&#39;, &#39;8.8&#39;, &#39;8.9&#39;, &#39;9&#39;, &#39;9.1&#39;, &#39;9.2&#39;, &#39;9.3&#39;, &#39;9.4&#39;, &#39;9.5&#39;, &#39;9.6&#39;, &#39;9.7&#39;]

修改的方法:在html中用下面代碼調用score(把內容轉化成json格式的)

data: {{ score|tojson }}

3.WordCloud應用

注意:pycharmsettings-project interpreter中找不到jieba模塊的話,可以考慮點擊
在這裏插入圖片描述

51job數據可視化項目

1.項目說明

1.1需求說明

項目名稱: 51job招聘網數據爬取分析平臺
項目背景: 隨着互聯網在中國的普及,網絡招聘景氣不斷攀升。它允許以更加靈活的交互方式,提供更加豐富 的信息資源。爲求職者提供更加多樣、精確、新鮮的招聘信息。

項目意義: 面對眼花繚亂的招聘信息,幫助求職者能夠通過招聘信息的數據可視化,分析招聘薪資等信息的分 布,明確自己的方向,找到適合的工作。

項目目標:
1、實現通過搜索對相關招聘的主要信息進行展示、下載(選做)。
2、對搜索出的信息進行地域、薪資、工作經驗、學歷、職責與要求等個方面的數據可視化分析。

1.2 技術要求

技術環境: Python3、urllib庫、flask框架、Echarts.js、wordcloud庫、sqlite3數據庫

分工要求: 每組4-5人,每人有一個菜單項,完成相應的數據可視化的功能展示,功能不能相同。
菜單項包括:首頁、列表頁(分頁選做)、Echarts圖表2-3個、詞雲0-1個、團隊介紹頁(選做)

考覈內容:
+ 基本功能:
1.使用爬蟲爬取51job數據至少1000條,並保存到數據庫中
2.應用flask框架完成網站搭建並能夠本地訪問
3.能夠引導用戶搜索關鍵詞,完成相應內容可視化的展現

+ 鼓勵創新:
1.數據呈現的多樣化:多種圖表形式
2.數據維度的設計:能夠從不同維度的數據分析,爲用戶提供更多價值
3.界面表現的美化

提交材料:
項目源碼
實驗報告

2.製作流程

2.1 爬取數據

爬取列表

  • 51job字符集爲gbk
    所以編碼解析需要設置爲gbk
  • url二次解析
    在51job上搜索中文(如大數據),在url顯示的其實是二次編碼後的結果,爲此我們需要模擬二次編碼,代碼如下
from urllib import parse
keyword = parse.quote('大數據')
newKW = parse.quote(keyword)  # 二次編碼
  • 使用選擇器實現文字提取
    如果不想使用正則表達式從html中提取數據,則可以通過選擇器提取數據,具體的,
    先通過F12定位到需要html中需要提取的區域,然後根據標籤遞進順序進行選擇,代碼如下:
html = open('jobList.heml', 'r')
bs = BeautifulSoup(html, 'html.parser')
eldiv = bs.select('.el > .t1 > span > a')
for link in eldiv:
	print(link.text.strip())

爬取詳情

  • 字符串截取:str.[n:m] 截取從下標n到m個字符
  • 分隔:str.split('|') 按照|分隔字符串爲列表
  • 去除特殊符號:str.replace('/r','') 替換'/r'爲空白字符串
  • 去除前後空格:str.strip()

2.2 數據保存

建議採用成熟的模版
前端頁面
模版推薦:
https://bootstrapmade.com/ 國外網站,需要翻牆才能訪問
https://colorlib.com/wp/templates/ 國內可訪問
https://colorlib.com/wp/free-bootstrap-admin-dashboard-templates/ 加載較慢

視頻裏面的模版:
https://startbootstrap.com/themes/sb-admin-2/

列表顯示
表單製作:

  • 表單傳值

2.3 搭建框架

2.4 製作圖表

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章