Python開發(fā)簡(jiǎn)單爬蟲(Python2.X版本,Eclipse工具)
一、爬蟲介紹


- 爬蟲調(diào)度端:?jiǎn)?dòng)、停止爬蟲,監(jiān)視爬蟲運(yùn)行情況
- URL管理器:管理將要爬取的URL和已經(jīng)爬取的URL
- 網(wǎng)頁下載器:下載URL指定的網(wǎng)頁,存儲(chǔ)成字符串
- 網(wǎng)頁解析器:提取有價(jià)值的數(shù)據(jù),提取關(guān)聯(lián)URL補(bǔ)充URL管理器

二、URL管理器


三、網(wǎng)頁下載器


(1)方法一


(2)方法二

- header:http頭信息
- data:用戶輸入信息

(3)方法三

- HTTPCookieProcessor:需登錄的網(wǎng)頁
- ProxyHandler:需代理訪問的網(wǎng)頁
- HTTPSHandler:加密訪問的網(wǎng)頁
- HTTPRedirectHandler:URL自動(dòng)跳轉(zhuǎn)的網(wǎng)頁

# coding:utf8 #出現(xiàn)編碼錯(cuò)誤時(shí)添加
import urllib2
import cookielib
url = "http://www.baidu.com"
print '第一種方法'
response1 = urllib2.urlopen(url)
print response1.getcode()
print len(response1.read())
print '第二種方法'
request = urllib2.Request(url)
request.add_header('user_agent', 'Mozilla/5.0')
response2 = urllib2.urlopen(request)
print response2.getcode()
print len(response2.read())
print '第三種方法'
cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
urllib2.install_opener(opener)
response3 = urllib2.urlopen(request)
print response3.getcode()
print cj
print response3.read()
四、網(wǎng)頁解析器


Python 自帶:
html.parser
第三方:BeautifulSoup,lxml

安裝beautifulsoup4
1.命令提示符中進(jìn)入安裝Python的文件夾中~\Python27\Scripts
2.輸入pip install beautifulsoup4




calss為Python的關(guān)鍵詞,所以用class_表示。

以字典形式可訪問節(jié)點(diǎn)所有屬性
參考:Python爬蟲利器二之Beautiful Soup的用法
# coding:utf8
import re
from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" id="link1">Elsie</a>,
<a class="sister" id="link2">Lacie</a> and
<a class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
soup = BeautifulSoup(html_doc,'html.parser',from_encoding='utf-8')
print '獲取所有的鏈接'
links = soup.find_all('a')
for link in links:
print link.name, link['href'], link.get_text()
print '獲取Lacie的 鏈接'
link_node = soup.find('a',)
print link_node.name, link_node['href'], link_node.get_text()
print '正則匹配'
link_node = soup.find('a',href = re.compile(r'ill'))
print link_node.name, link_node['href'], link_node.get_text()
print '獲取p段落文字'
p_node = soup.find('a',class_ = "title")
print p_node.name, p_node.get_text()
結(jié)果:
獲取所有的鏈接
a http://example.com/elsie Elsie
a http://example.com/lacie Lacie
a http://example.com/tillie Tillie
獲取Lacie的 鏈接
a http://example.com/lacie Lacie
正則匹配
a http://example.com/tillie Tillie
獲取p段落文字
p The Dormouse's story
Eclipse:
ctrl+shift+M或Ctrl+Shift+o或Ctrl+1可以自動(dòng)導(dǎo)入相應(yīng)的包或創(chuàng)建相應(yīng)的類或方法。
五、實(shí)例


觀察目標(biāo),定制策略,策略要根據(jù)目標(biāo)的變化實(shí)時(shí)更新。

精通Python網(wǎng)絡(luò)爬蟲(Python3.X版本,PyCharm工具)
一、爬蟲類型
- 通用網(wǎng)絡(luò)爬蟲:全網(wǎng)爬蟲。由初始URL集合、URL隊(duì)列、頁面爬行模塊、頁面分析模塊、頁面數(shù)據(jù)庫、鏈接過濾模塊等構(gòu)成。
- 聚焦網(wǎng)絡(luò)爬蟲:主題爬蟲。由初始URL集合、URL隊(duì)列、頁面爬行模塊、頁面分析模塊、頁面數(shù)據(jù)庫、鏈接過濾模塊,內(nèi)容評(píng)價(jià)模塊、鏈接評(píng)價(jià)模塊等構(gòu)成。
- 增量網(wǎng)絡(luò)爬蟲:增量式更新,盡可能爬取新頁面(更新改變的部分)。
- 深層網(wǎng)絡(luò)爬蟲:隱藏在表單后,需要提交一定關(guān)鍵詞才能獲取的頁面。URL列表、LVS列表(LVS指標(biāo)簽/數(shù)值集合,即填充表單的數(shù)據(jù)源)、爬行控制器、解析器、LVS控制器、表單分析器、表單處理器、響應(yīng)分析器等構(gòu)成。
二、核心技術(shù)
PyCharm常用快捷鍵:
Alt+Enter:快速導(dǎo)入包
Ctrl+z:撤銷,Ctrl+Shift+z:反撤銷
(1)Urllib庫
1)Python2.X與Python3.X區(qū)別
| Python2.X | Python3.X |
|---|---|
import urllib2 |
import urllib.requset, urllib.error |
import urllib |
import urllib.requset, urllib.error, urllib.parse |
urllib2.urlopen |
urllib.request.urlopen |
urllib.urlencode |
urllib.parse.urlencode |
urllib.quote |
urllib.request.quote |
urllib.CookieJar |
http.CookieJar |
urllib.Request |
urllib.request.Request |
2)快速爬取網(wǎng)頁
import urllib.request
# 爬取百度網(wǎng)頁內(nèi)容
file = urllib.request.urlopen("http://www.baidu.com", timeout=30) # timeout超時(shí)設(shè)置,單位:秒
data = file.read() #讀取文件全部?jī)?nèi)容,字符串類型
dataline = file.readline() #讀取文件一行內(nèi)容
datalines = file.readlines() #讀取文件全部?jī)?nèi)容,列表類型
# 以html格式存儲(chǔ)到本地
fhandle = open("/.../1.html","wb")
fhandle.write(data)
fhandle.close()
# 快捷存儲(chǔ)到本地
filename = urllib.request.urlretrieve("http://www.baidu.com",filename="/.../1.html")
urllib.request.urlcleanup() #清除緩存
# 其他常用方法
file.getcode() #響應(yīng)狀態(tài)碼,200為鏈接成功
file.geturl() #爬取的源網(wǎng)頁
# URL編碼(當(dāng)URL中存在漢字等不符合標(biāo)準(zhǔn)的字符時(shí)需要編碼后爬取)
urllib.request.quote("http://www.baidu.com") # http%3A//www.baidu.com
# URL解碼
urllib.request.unquote("http%3A//www.baidu.com") # http://www.baidu.com
注意:URL中存在漢字如
https://www.baidu.com/s?wd=電影,爬取該URL時(shí)實(shí)際傳入U(xiǎn)RL應(yīng)該是"https://www.baidu.com/s?wd=" + urllib.request.quote("電影"),而不應(yīng)該是urllib.request.quote("https://www.baidu.com/s?wd=電影")
3)瀏覽器模擬(應(yīng)對(duì)403禁止訪問)
import urllib.request
url = "http://baidu.com"
# 方法一
headers = ("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0")
opener = urllib.request.build_opener()
opener.addheaders = [headers]
data = opener.open(url).read()
# 方法二
req = urllib.request.Request(url)
req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0")
data = urllib.request.urlopen(req).read()
4)POST請(qǐng)求
import urllib.request
import urllib.parse
url = "http://www.iqianyue.com/mypost" # 測(cè)試網(wǎng)站
# 將數(shù)據(jù)使用urlencode編碼處理后,使用encode()設(shè)置為utf-8編碼
postdata = urllib.parse.urlencode({"name": "abc", "pass": "111"}).encode("utf-8")
req = urllib.request.Request(url, postdata)
req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0")
data = urllib.request.urlopen(req).read()
注意:
postdata = urllib.parse.urlencode({"name": "abc", "pass": "111"}).encode("utf-8"),必須encode("utf-8")編碼后才可使用,實(shí)際結(jié)果為b'name=abc&pass=111',未編碼結(jié)果為name=abc&pass=111
5)代理服務(wù)器設(shè)置(應(yīng)對(duì)IP被屏蔽、403)
def use_proxy(proxy_add, url):
import urllib.request
proxy = urllib.request.ProxyHandler({'http': proxy_add})
opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler)
# 創(chuàng)建全局默認(rèn)opener對(duì)象,這樣使用urlopen()也會(huì)使用該對(duì)象
urllib.request.install_opener(opener)
# 解碼類型與網(wǎng)頁編碼格式一致
data = urllib.request.urlopen(url).read().decode("gb2312")
return data
# 代理IP可用百度搜索
data = use_proxy("116.199.115.79:80", "http://www.baidu.com")
print(data)
注意:
encode():編碼,decode():解碼
- 例如(Python3.X):
u = '中文'
str = u.encode('utf-8') # 結(jié)果:b'\xe4\xb8\xad\xe6\x96\x87',為字節(jié)類型
u1 = str.decode('utf-8') # 結(jié)果:中文- 過程:
str(unicode) --[encode('utf-8')]--> bytes --[decode('utf-8')]--> str(unicode)
6)DebugLog調(diào)試日志
import urllib.request
httphd = urllib.request.HTTPHandler(debuglevel=1)
httpshd = urllib.request.HTTPSHandler(debuglevel=1)
opener = urllib.request.build_opener(httphd, httpshd)
urllib.request.install_opener(opener)
data = urllib.request.urlopen("http://www.baidu.com")
運(yùn)行結(jié)果:
send: b'GET / HTTP/1.1\r\nAccept-Encoding: identity\r\nHost: www.baidu.com\r\nUser-Agent: Python-urllib/3.6\r\nConnection: close\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Date header: Content-Type header: Transfer-Encoding header: Connection header: Vary header: Set-Cookie header: Set-Cookie header: Set-Cookie header: Set-Cookie header: Set-Cookie header: Set-Cookie header: P3P header: Cache-Control header: Cxy_all header: Expires header: X-Powered-By header: Server header: X-UA-Compatible header: BDPAGETYPE header: BDQID header: BDUSERID
7)異常處理
URLError:1)連接不上服務(wù)器。2)遠(yuǎn)程URL不存在。3)無網(wǎng)絡(luò)。4)HTTPError:
200:OK
301:Moved Permanently——重定向到新的URL
302:Found——重定向到臨時(shí)的URL
304:Not Modified——請(qǐng)求資源未更新
400:Bad Request——非法請(qǐng)求
401:Unauthorized——請(qǐng)求未經(jīng)授權(quán)
403:Forbidden——禁止訪問
404:Not Found——未找到對(duì)應(yīng)頁面
500:Internal Server Error——服務(wù)器內(nèi)部錯(cuò)誤
501:Not Implemented——服務(wù)器不支持實(shí)現(xiàn)請(qǐng)求所需要的功能
import urllib.error
import urllib.request
try:
urllib.request.urlopen("http://www.baidu.com")
except urllib.error.URLError as e:
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
(2)正則表達(dá)式

1)基本語法(適用其他)
-
1.單個(gè)字符匹配
[...]匹配字符集內(nèi)的任意字符
\w包括[a-zA-Z0-9]即匹配所以大小寫字符及數(shù)字,以及下劃線
-
2.多個(gè)字符匹配
因?yàn)?code>*匹配前個(gè)字符0到無限次,所以
*?匹配前個(gè)字符0次,既不匹配前個(gè)字符。
因?yàn)?code>+匹配前個(gè)字符1到無限次,所以+?匹配前個(gè)字符1次。
因?yàn)?code>?匹配前個(gè)字符0或1次,所以??匹配前個(gè)字符0次,既不匹配前個(gè)字符。
-
3.邊界匹配
-
4.分組匹配
\<num>引用編號(hào)為num的分組匹配的字符串:
代碼1:
re.match(r'<(book>)(python)</\1\2','<book>python</book>python').group()
結(jié)果:<book>python</book>python
代碼2:re.match(r'<(book>)(python)</\1\2','<book>python</book>python').groups()
結(jié)果:('book>','python')
解釋:
.groups()方法返回分組匹配的字符串集合,指總體匹配模式中()內(nèi)的分組匹配模式匹配的結(jié)果集。代碼1中'<(book>)(python)</\1\2'為總體匹配模式,其中有(book>)和(python)兩個(gè)分組匹配模式,代碼2結(jié)果就為這兩個(gè)分組匹配模式匹配的結(jié)果集,\<num>就是通過num來引用該結(jié)果集中的字符串,\1為book>,\2為python。
用
(?P<name>)和(?P=name)替代,代碼1還可以寫為:
re.match(r'<(?P<mark1>book>)(?P<mark2>python)</(?P=mark1)(?P=mark2)','<book>python</book>python').group()
- 5.模式修改
| 符號(hào) | 含義 |
|---|---|
I |
匹配時(shí)忽略大小寫 |
M |
多行匹配 |
L |
做本地化識(shí)別匹配 |
U |
根據(jù)Unicode字符及解析字符 |
S |
讓.匹配包括換行符,使.可以匹配任意字符 |
2)re模塊

import re
str = ‘imooc python’
pa = re.compile(r'imooc') #匹配‘imooc’字符串
ma = pa.match(str)
# 等價(jià)于
ma = re.match(r'imooc', str)
ma.string #被匹配字符串
ma.re #匹配模式(pa值)
ma.group() #匹配結(jié)果
ma.span() #匹配位置
pa = re.compile(r'imooc', re.I) #匹配‘imooc’字符串,不管大小寫
# 上述最終可寫為
ma = re.match(r'imooc', 'imooc python', re.I)
樣式字符串前r的用法:
(1)帶上r,樣式字符串為原字符串,后面的樣式字符串是什么匹配什么,里面即使有轉(zhuǎn)義字符串也按普通字符串匹配。
(2)不帶r,樣式字符串無轉(zhuǎn)義字符串不影響,有轉(zhuǎn)義字符串需考慮轉(zhuǎn)義字符串進(jìn)行匹配。
例子中r'imooc\\n'相當(dāng)于imooc\\n,'imooc\\n'相當(dāng)于imooc\n,因?yàn)?code>'\\'為轉(zhuǎn)義字符串時(shí)相當(dāng)于'\'
march從頭開始匹配,找出字符串開頭符合匹配樣式的部分,開頭無符合返回NoneType
seach從頭開始匹配,找出字符串內(nèi)第一個(gè)符合匹配樣式的部分并返回,字符串內(nèi)無符合返回NoneType


sub()參數(shù)中repl可以是用來替代的字符串,也可以是一個(gè)函數(shù)且該函數(shù)需返回一個(gè)用來替換的字符串。count為替換次數(shù),默認(rèn)為0,為都替換。
re.sub(r'\d+','100','imooc videnum=99')
re.sub(r'\d+',lambda x: str(int(x.group())+1),'imooc videnum=99')
結(jié)果:'imooc videnum=100'
lambda x: str(int(x.group())+1)為匿名函數(shù),其中冒號(hào)前的x為函數(shù)參數(shù),默認(rèn)傳入匹配的結(jié)果對(duì)象,需要用.group()方法獲取結(jié)果字符串。冒號(hào)后算式的結(jié)果為返回值。也可以寫成:
def add(x):
val = x.group()
num = int(val)+1
return str(num)
re.sub(r'\d+',add,'imooc videnum=99')
(3)Cookie用法(應(yīng)對(duì)模擬登陸)
import urllib.request
import urllib.parse
import http.cookiejar
# 創(chuàng)建CookieJar對(duì)象
cjar = http.cookiejar.CookieJar()
# 使用HTTPCookieProcessor創(chuàng)建cookie處理器,并以其為參數(shù)創(chuàng)建opener對(duì)象
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cjar))
# 將opener安裝為全局
urllib.request.install_opener(opener)
# 網(wǎng)站登錄頁
url1 = 'http://xxx.com/index.php/login/login_new/'
# 登陸所需要POST的數(shù)據(jù)
postdata = urllib.parse.urlencode({
'username': 'xxx',
'password': 'xxx'
}).encode("utf-8")
req = urllib.request.Request(url1, postdata)
# 網(wǎng)站登陸后才能訪問的網(wǎng)頁
url2 = 'http://xxx.com/index.php/myclass'
# 登陸網(wǎng)站
file1 = urllib.request.urlopen(req)
# 爬取目標(biāo)網(wǎng)頁信息
file2 = urllib.request.urlopen(url2).read()
(4)多線程與隊(duì)列
# 多線程基礎(chǔ)
import threading
class A(threading.Thread):
def __init__(self):
# 初始化該線程
threading.Thread.__init__(self)
def run(self):
# 該線程要執(zhí)行的內(nèi)容
for i in range(10):
print("線程A運(yùn)行")
class B(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
for i in range(10):
print("線程B運(yùn)行")
t1 = A()
t1.start()
t2 = B()
t2.start()
# 隊(duì)列基礎(chǔ)(先進(jìn)先出)
import queue
# 創(chuàng)建隊(duì)列對(duì)象
a = queue.Queue()
# 數(shù)據(jù)傳入隊(duì)列
a.put("hello")
a.put("php")
a.put("python")
a.put("bye")
# 結(jié)束數(shù)據(jù)傳入
a.task_done()
for i in range(4):
# 取出數(shù)據(jù)
print(a.get())
(5)瀏覽器偽裝
Headers信息:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
-
Accept:瀏覽器支持內(nèi)容類型,支持優(yōu)先順序從左往右依次排序 -
text/html:HTML文檔 -
application/xhtml+xml:XHTML文檔 -
application/xml:XML文檔
Accept-Encoding:gzip, deflate, sdch(設(shè)置該字段,從服務(wù)器返回的是對(duì)應(yīng)形式的壓縮代碼(瀏覽器會(huì)自動(dòng)解壓縮),因此可能出現(xiàn)亂碼)
-
Accept-Encoding:瀏覽器支持的壓縮編碼方式 -
deflate:一種無損數(shù)據(jù)壓縮的算法
Accept-Language:zh-CN,zh;q=0.8
-
Accept-Language:支持的語言類型 -
zh-CN:zh中文,CN簡(jiǎn)體 -
en-US:英語(美國)
Connection:keep-alive
-
Connection:客戶端與服務(wù)端連接類型 -
keep-alive:持久性連接 -
close:連接斷開
Referer:http://123.sogou.com/(某些反爬蟲網(wǎng)址可能檢驗(yàn)該字段,一般可以設(shè)置為要爬取網(wǎng)頁的域名地址或?qū)?yīng)網(wǎng)址的主頁地址)
-
Referer:來源網(wǎng)址
·
.addheaders方法傳入格式為:[('Connection','keep-alive'),("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0"),...]
三、Scrapy框架
(1)常見爬蟲框架
- Scrapy框架:
https://scrapy.org/ - Crawley框架
- Portia框架:有網(wǎng)頁版
- newspaper框架
- python-goose框架
(2)安裝Scrapy
Python2.X和Python3.X同時(shí)安裝,命令提示符:
py -2:?jiǎn)?dòng)Python2.X
py -3:?jiǎn)?dòng)Python3.X
py -2 -m pip install ...:使用Python2.X pip安裝
py -3 -m pip install ...:使用Python3.X pip安裝安裝超時(shí):
手動(dòng)指定源,在pip后面跟-i,命令如下:
pip install packagename -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
pipy國內(nèi)鏡像目前有:
豆瓣 http://pypi.douban.com/simple/
阿里云 http://mirrors.aliyun.com/pypi/simple/
中國科技大學(xué) https://pypi.mirrors.ustc.edu.cn/simple/
清華大學(xué) https://pypi.tuna.tsinghua.edu.cn/simple/
華中理工大學(xué) http://pypi.hustunique.com/
山東理工大學(xué) http://pypi.sdutlinux.org/出現(xiàn)如下錯(cuò)誤:
error:Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools
解決方案:
在http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted下載twisted對(duì)應(yīng)版本的whl文件,cp后面是python版本,amd64代表64位,以Python位數(shù)為準(zhǔn)
運(yùn)行命令:
pip install C:\xxx\Twisted-17.5.0-cp36-cp36m-win_amd64.whl安裝成功后運(yùn)行出現(xiàn)
No module named 'win32api'錯(cuò)誤:
在https://sourceforge.net/projects/pywin32/files%2Fpywin32/下載安裝對(duì)應(yīng)pywin32即可
(3)Scrapy應(yīng)用(命令提示符輸入)
1)創(chuàng)建項(xiàng)目
scrapy startproject myscrapy:創(chuàng)建名為myscrapy的爬蟲項(xiàng)目,自動(dòng)生成如下目錄


目錄結(jié)構(gòu):
-
myscrapy/scrapy.cfg:爬蟲項(xiàng)目配置文件 -
myscrapy/myscrapy/items.py:數(shù)據(jù)容器文件,定義獲取的數(shù)據(jù) -
myscrapy/myscrapy/pipelines.py:管道文件,對(duì)items定義的數(shù)據(jù)進(jìn)行加工處理 -
myscrapy/myscrapy/settings.py:設(shè)置文件 -
myscrapy/myscrapy/spiders:放置爬蟲文件 -
myscrapy/myscrapy/middleware.py:下載中間件文件
參數(shù)控制:

-
scrapy startproject --logfile="../logf.log" myscrapy
在創(chuàng)建myscrapy爬蟲項(xiàng)目同時(shí)在指定地址創(chuàng)建名為logf的日志文件 -
scrapy startproject --loglevel=DEBUG myscrapy
創(chuàng)建項(xiàng)目同時(shí)指定日志信息的等級(jí)為DEBUG模式(默認(rèn)),等級(jí)表如下:
| 等級(jí)名 | 含義 |
|---|---|
CRITICAL |
發(fā)生最嚴(yán)重的錯(cuò)誤 |
ERROR |
發(fā)生必須立即處理的錯(cuò)誤 |
WARNING |
出現(xiàn)警告信息,存在潛在錯(cuò)誤 |
INFO |
輸出提示信息 |
DEBUG |
輸出調(diào)試信息,常用于開發(fā)階段 |
-
scrapy startproject --nolog myscrapy
創(chuàng)建項(xiàng)目同時(shí)指定不輸出日志
2)常用工具命令
全局命令:(項(xiàng)目文件夾外scrapy -h)

-
scrapy fetch http://www.baidu.com:顯示爬取網(wǎng)站的過程 -
scrapy fetch --headers --nolog http://www.baidu.com:顯示頭信息不顯示日志信息 -
scrapy runspider 爬蟲文件.py -o xxx/xxx.xxx:運(yùn)行指定爬蟲文件并將爬取結(jié)果存儲(chǔ)在指定文件內(nèi) -
scrapy setting --get BOT_NAME:項(xiàng)目?jī)?nèi)執(zhí)行為項(xiàng)目名,項(xiàng)目外執(zhí)行為scrapybot -
scrapy shell http://www.baidu.com --nolog:爬取百度首頁創(chuàng)建一個(gè)交互終端環(huán)境并設(shè)置為不輸出日志信息。
項(xiàng)目命令:(項(xiàng)目文件夾內(nèi)scrapy -h)

-
scrapy bench:測(cè)試本地硬件性能 -
scrapy genspider -l:查看可使用的爬蟲模板

-
scrapy genspider -d 模板名:查看爬蟲模板內(nèi)容 -
scrapy genspider -t 模板名 爬蟲名 要爬取的網(wǎng)站域名:快速創(chuàng)建一個(gè)爬蟲文件 -
scrapy check 爬蟲名:對(duì)爬蟲文件進(jìn)行合同測(cè)試 -
scrapy crawl 爬蟲名:?jiǎn)?dòng)爬蟲 -
scrapy list:顯示可以使用的爬蟲文件 -
scrapy edit 爬蟲名:編輯爬蟲文件(Windows下執(zhí)行有問題) -
scrapy parse 網(wǎng)站URL:獲取指定URL網(wǎng)站內(nèi)容,并使用對(duì)應(yīng)爬蟲文件處理分析,可設(shè)置的常用參數(shù)如下:
| 參數(shù) | 含義 |
|---|---|
--spider==SPIDER |
指定某個(gè)爬蟲文件進(jìn)行處理 |
-a NAME=VALUE |
設(shè)置爬蟲文件參數(shù) |
--pipelines |
通過pipelines處理items |
--nolinks |
不展示提取到的鏈接信息 |
--noitems |
不展示得到的items |
--nocolour |
輸出結(jié)果顏色不高亮 |
--rules,-r |
使用CrawlSpider規(guī)則處理回調(diào)函數(shù) |
--callback=CALLBACK,-c CALLBACK |
指定spider中用于處理返回的響應(yīng)的回調(diào)函數(shù) |
--depth=DEPTH,-d DEPTH |
設(shè)置爬取深度,默認(rèn)為1 |
--verbose,-v |
顯示每層的詳細(xì)信息 |
3)Items編寫
import scrapy
class MyscrapyItem(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field()
...
格式:數(shù)據(jù)名 = scrapy.Field()
實(shí)例化:item = MyscrapyItem(name = "xxx",...)
調(diào)用:item["name"]、item.keys()、 item.items()(可以看做字典使用)
4)Spider編寫(BasicSpider)
# -*- coding: utf-8 -*-
import scrapy
class MyspiderSpider(scrapy.Spider):
name = 'myspider' # 爬蟲名
allowed_domains = ['baidu.com']
start_urls = ['http://baidu.com/']
def parse(self, response):
pass
-
allowed_domains:允許爬取的域名,當(dāng)開啟OffsiteMiddleware時(shí),非允許的域名對(duì)應(yīng)的網(wǎng)址會(huì)自動(dòng)過濾,不再跟進(jìn)。 -
start_urls:爬取的起始網(wǎng)址,如果沒有指定爬取的URL網(wǎng)址,則從該屬性中定義的網(wǎng)址開始進(jìn)行爬取,可指定多個(gè)起始網(wǎng)址,網(wǎng)址間用逗號(hào)隔開。 -
parse方法:如果沒有特別指定回調(diào)函數(shù),該方法是處理Scrapy爬蟲爬行到的網(wǎng)頁響應(yīng)(response)的默認(rèn)方法,通過該方法,可以對(duì)響應(yīng)進(jìn)行處理并返回處理后的數(shù)據(jù),同時(shí)該方法也負(fù)責(zé)鏈接的跟進(jìn)。
| 其他方法 | 含義 |
|---|---|
start_requests() |
該方法默認(rèn)讀取start_urls屬性中定義的網(wǎng)址(也可自定義),為每個(gè)網(wǎng)址生成一個(gè)Request請(qǐng)求對(duì)象,并返回可迭代對(duì)象 |
make_requests_from_url(url) |
該方法會(huì)被start_requests() 調(diào)用,負(fù)責(zé)實(shí)現(xiàn)生成Request請(qǐng)求對(duì)象 |
close(reason) |
關(guān)閉Spider時(shí)調(diào)用 |
log(message[,level, component]) |
實(shí)現(xiàn)在Spider中添加log |
__init__() |
負(fù)責(zé)爬蟲初始化的構(gòu)造函數(shù) |
# -*- coding: utf-8 -*-
import scrapy
from myscrapy.items import MyscrapyItem
class MyspiderSpider(scrapy.Spider):
name = 'myspider'
allowed_domains = ['baidu.com']
start_urls = ['http://baidu.com/']
my_urls = ['http://baidu.com/', 'http://baidu.com/']
# 重寫該方法可讀取自己定義的URLS,不重寫時(shí)默認(rèn)從start_urls中讀取起始網(wǎng)址
def start_requests(self):
for url in self.my_urls:
# 調(diào)用默認(rèn)make_requests_from_url()生成具體請(qǐng)求并迭代返回
yield self.make_requests_from_url(url)
def parse(self, response):
item = MyscrapyItem()
item["name"] = response.xpath("/html/head/title/text()")
print(item["name"])
5)XPath基礎(chǔ)
-
/:選擇某個(gè)標(biāo)簽,可多層標(biāo)簽查找 -
//:提取某個(gè)標(biāo)簽的所有信息 -
test():獲取該標(biāo)簽的文本信息 -
//Z[@X="Y"]:獲取所有屬性X的值是Y的<Z>標(biāo)簽的內(nèi)容

- 返回一個(gè)SelectorList 對(duì)象
- 返回一個(gè)list、里面是一些提取的內(nèi)容
- 返回2中l(wèi)ist的第一個(gè)元素(如果list為空拋出異常)
- 返回1中SelectorList里的第一個(gè)元素(如果list為空拋出異常),和3達(dá)成的效果一致
- 4返回的是一個(gè)str, 所以5會(huì)返回str的第一個(gè)字符
6)Spider類參數(shù)傳遞(通過-a選項(xiàng)實(shí)現(xiàn)參數(shù)的傳遞)
# -*- coding: utf-8 -*-
import scrapy
from myscrapy.items import MyscrapyItem
class MyspiderSpider(scrapy.Spider):
name = 'myspider'
allowed_domains = ['baidu.com']
start_urls = ['http://baidu.com/']
# 重寫初始化方法,并設(shè)置參數(shù)myurl
def __init__(self, myurl=None, *args, **kwargs):
super(MyspiderSpider, self).__init__(*args, **kwargs)
myurllist = myurl.split(",")
# 輸出要爬的網(wǎng)站
for i in myurllist:
print("爬取網(wǎng)站:%s" % i)
# 重新定義start_urls屬性
self.start_urls = myurllist
def parse(self, response):
item = MyscrapyItem()
item["name"] = response.xpath("/html/head/title/text()")
print(item["name"])
命令行:scrapy crawl myspider -a myurl=http://www.sina.com.cn,http://www.baidu.com --nolog
7)XMLFeedSpider
# -*- coding: utf-8 -*-
from scrapy.spiders import XMLFeedSpider
class MyxmlSpider(XMLFeedSpider):
name = 'myxml'
allowed_domains = ['sina.com.cn']
start_urls = ['http://sina.com.cn/feed.xml']
iterator = 'iternodes' # you can change this; see the docs
itertag = 'item' # change it accordingly
def parse_node(self, response, selector):
i = {}
# i['url'] = selector.select('url').extract()
# i['name'] = selector.select('name').extract()
# i['description'] = selector.select('description').extract()
return i
-
iterator:設(shè)置迭代器,默認(rèn)iternodes(基于正則表達(dá)式的高性能迭代器),此外還有html、xml -
itertag:設(shè)置開始迭代的節(jié)點(diǎn) -
parse_node(self, response, selector):在節(jié)點(diǎn)與所提供的標(biāo)簽名相符合的時(shí)候被調(diào)用,可進(jìn)行信息的提取和處理操作
| 其他屬性或方法 | 含義 |
|---|---|
namespaces |
以列表形式存在,主要定義在文檔中會(huì)被爬蟲處理的可用命名空間 |
adapt_response(response) |
主要在spider分析響應(yīng)(Response)前被調(diào)用 |
process_results(response, results) |
主要在spider返回結(jié)果時(shí)被調(diào)用,對(duì)結(jié)果在返回前進(jìn)行最后處理 |
8)CSVFeedSpider
CSV:一種簡(jiǎn)單、通用的文件格式,其存儲(chǔ)的數(shù)據(jù)可以與表格數(shù)據(jù)相互轉(zhuǎn)化。最原始的形式是純文本形式,列之間通過,間隔,行之間通過換行間隔。
# -*- coding: utf-8 -*-
from scrapy.spiders import CSVFeedSpider
class MycsvSpider(CSVFeedSpider):
name = 'mycsv'
allowed_domains = ['iqianyue.com']
start_urls = ['http://iqianyue.com/feed.csv']
# headers = ['id', 'name', 'description', 'image_link']
# delimiter = '\t'
# Do any adaptations you need here
#def adapt_response(self, response):
# return response
def parse_row(self, response, row):
i = {}
#i['url'] = row['url']
#i['name'] = row['name']
#i['description'] = row['description']
return i
-
headers:存放CSV文件包含的用于提取字段行信息的列表 -
delimiter:主要存放字段之間的間隔符,csv文件以,間隔 -
parse_row(self, response, row):用于接收Response對(duì)象,并進(jìn)行相應(yīng)處理
9)CrawlSpider(自動(dòng)爬取)
class MycrawlSpider(CrawlSpider):
name = 'mycrawl'
allowed_domains = ['sohu.com']
start_urls = ['http://sohu.com/']
# 自動(dòng)爬取規(guī)則
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
def parse_item(self, response):
i = {}
#i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
#i['name'] = response.xpath('//div[@id="name"]').extract()
#i['description'] = response.xpath('//div[@id="description"]').extract()
return i
-
rules:設(shè)置自動(dòng)爬取規(guī)則,規(guī)則Rule的參數(shù)如下: -
LinkExtractor:鏈接提取器,用來提取頁面中滿足條件的鏈接,以供下次爬取使用,可設(shè)置的參數(shù)如下
| 參數(shù)名 | 含義 |
|---|---|
allow |
提取符合對(duì)應(yīng)正則表達(dá)式的鏈接 |
deny |
不提取符合對(duì)應(yīng)正則表達(dá)式的鏈接 |
restrict_xpaths |
使用XPath表達(dá)式與allow共同作用,提取出同時(shí)符合兩者的鏈接 |
allow_domains |
允許提取的域名,該域名下的鏈接才可使用 |
deny_domains |
禁止提取的域名,限制不提取該域名下的鏈接 |
-
callback='parse_item':處理的回調(diào)方法 -
follow=True:是否跟進(jìn)。CrawlSpider爬蟲會(huì)根據(jù)鏈接提取器中設(shè)置的規(guī)則自動(dòng)提取符合條件的網(wǎng)頁鏈接,提取之后再自動(dòng)的對(duì)這些鏈接進(jìn)行爬取,形成循環(huán),如果鏈接設(shè)置為跟進(jìn),則會(huì)一直循環(huán)下去,如果設(shè)置為不跟進(jìn),則第一次循環(huán)后就會(huì)斷開。
10)避免爬蟲被禁止(settings.py內(nèi)設(shè)置)
- 禁止Cookie:(應(yīng)對(duì)通過用戶Cookie信息對(duì)用戶識(shí)別和分析的網(wǎng)站)
# Disable cookies (enabled by default)
COOKIES_ENABLED = False
- 設(shè)置下載延時(shí):(設(shè)置爬取的時(shí)間間隔,應(yīng)對(duì)通過網(wǎng)頁訪問(爬?。╊l率進(jìn)行分析的網(wǎng)站)
# Configure a delay for requests for the same website (default: 0)
# See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
- IP池:(應(yīng)對(duì)檢驗(yàn)用戶IP的網(wǎng)站)
在middlewares.py中或新創(chuàng)建一個(gè)Python文件中編寫:
import random
from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware
class IPPOOLS(HttpProxyMiddleware):
myIPPOOL = ["183.151.144.46:8118",
"110.73.49.52:8123",
"123.55.2.126:808"]
# process_request()方法,主要進(jìn)行請(qǐng)求處理
def process_request(self, request, spider):
# 隨機(jī)選擇一個(gè)IP
thisip = random.choice(self.myIPPOOL)
# 將IP添加為具體代理,用該IP進(jìn)行爬取
request.meta["proxy"] = "http://" + thisip
# 輸出觀察
print('當(dāng)前使用IP:%s' % request.meta["proxy"])
設(shè)置為默認(rèn)下載中間件:
# Enable or disable downloader middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 123,
# 格式:'下載中間件所在目錄.下載中間件文件名.下載中間件內(nèi)部要使用的類':數(shù)字(有規(guī)定)
'myscrapy.middlewares.IPPOOLS': 125
}
- 用戶代理池
在middlewares.py中或新創(chuàng)建一個(gè)Python文件中編寫:
import random
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
class UAPOOLS(UserAgentMiddleware):
myUApool = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
]
def process_request(self, request, spider):
thisUA = random.choice(self.myUApool)
request.headers.setdefault('User-Agent', thisUA)
print("當(dāng)前使用UA: %s" % thisUA)
設(shè)置為默認(rèn)下載中間件:
# Enable or disable downloader middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
'myscrapy.middlewares.UAPOOLS': 1,
'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': 2,
}
(4)Scrapy核心框架

- Scrapy引擎:框架核心,控制整個(gè)數(shù)據(jù)處理流程,以及觸發(fā)一些事務(wù)處理。
- 調(diào)度器:存儲(chǔ)待爬取的網(wǎng)址,并確定網(wǎng)址優(yōu)先級(jí),同時(shí)會(huì)過濾一些重復(fù)的網(wǎng)址。
- 下載器:對(duì)網(wǎng)頁資源進(jìn)行高速下載,然后將這些數(shù)據(jù)傳遞給Scrapy引擎,再由引擎?zhèn)鬟f給爬蟲進(jìn)行處理。
- 下載中間件:下載器與引擎間的特殊組件,處理其之間的通信。
- 爬蟲:接收并分析處理引擎的Response響應(yīng),提取所需數(shù)據(jù)。
- 爬蟲中間件:爬蟲與引擎間的特殊組件,處理其之間的通信。
- 實(shí)體管道:接收爬蟲組件中提取的數(shù)據(jù),如:清洗、驗(yàn)證、存儲(chǔ)至數(shù)據(jù)庫等
(5)Scrapy輸出與存儲(chǔ)
1)中文存儲(chǔ)
setting.py設(shè)置pipelines
# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'myscrapy.pipelines.MyscrapyPipeline': 300,
}
import codecs
class MyscrapyPipeline(object):
def __init__(self):
# 以寫入的方式創(chuàng)建或打開要存入數(shù)據(jù)的文件
self.file = codecs.open('E:/xxx/mydata.txt',
'wb',
encoding="utf-8")
# 主要處理方法,默認(rèn)自動(dòng)調(diào)用
def process_item(self, item, spider):
content = str(item) + '\n'
self.file.write(content)
return item
# 關(guān)閉爬蟲時(shí)調(diào)用
def close_spider(self, spider):
self.file.close()
注意:要想執(zhí)行
process_item(),爬蟲文件parse()方法中必須返回item:yield item
2)Json輸出
import codecs
import json
class MyscrapyPipeline(object):
def __init__(self):
print("創(chuàng)建pip")
# 以寫入的方式創(chuàng)建或打開要存入數(shù)據(jù)的文件
self.file = codecs.open('E:/PycharmProjects/untitled/myscrapy/data/mydata.txt',
'wb',
encoding="utf-8")
def process_item(self, item, spider):
js = json.dumps(dict(item), ensure_ascii=False)
content = js + '\n'
self.file.write(content)
return item
# 關(guān)閉爬蟲時(shí)調(diào)用
def close_spider(self, spider):
self.file.close()
注意:
- 爬蟲文件
parse()方法中,由response.xpath("xxx/text()")返回的SelectorList 對(duì)象不能轉(zhuǎn)換為Json類型,需要response.xpath("xxx/text()").extract()轉(zhuǎn)化為字符串列表類型才可轉(zhuǎn)化為Json類型。json.dumps(dict(item), ensure_ascii=False):進(jìn)行json.dumps()序列化時(shí),中文信息默認(rèn)使用ASCII編碼,當(dāng)設(shè)定不使用ASCII編碼時(shí),中文信息就可以正常顯示
3)數(shù)據(jù)庫操作
- 安裝:
pip install pymysql3 - 導(dǎo)入:
import pymysql - 鏈接MySQL:
conn = pymysql.connect(host="主機(jī)名", user="賬號(hào)", passwd="密碼"[, db="數(shù)據(jù)庫名"]) - SQL語句執(zhí)行:
conn.query("SQL語句") - 查看表內(nèi)容:
# cursor()創(chuàng)建游標(biāo)
cs = conn.cursor()
# execute()執(zhí)行對(duì)應(yīng)select語句
cs.execute("select * from mytb")
# 遍歷
for i in cs:
print("當(dāng)前是第"+str(cs.rownumber)+"行")
print(i[x])
四、Scrapy文檔實(shí)例
(1)循環(huán)爬取http://quotes.toscrape.com/網(wǎng)站
import scrapy
class MyxpathSpider(scrapy.Spider):
name = 'myxpath'
allowed_domains = ['toscrape.com']
start_urls = ['http://quotes.toscrape.com/']
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').extract_first(),
'author': quote.xpath('span/small/text()').extract_first(),
}
next_page = response.css('li.next a::attr("href")').extract_first()
if next_page is not None:
yield response.follow(next_page, self.parse)
循環(huán)爬取時(shí),注意循環(huán)的下個(gè)網(wǎng)頁需在
allowed_domains域名下,否則會(huì)被過濾,從而無法循環(huán)






