Python網絡爬蟲

Python開發簡單爬蟲(Python2.X版本,Eclipse工具)

一、爬蟲介紹


  • 爬蟲調度端:啟動、停止爬蟲,監視爬蟲運行情況
  • URL管理器:管理將要爬取的URL和已經爬取的URL
  • 網頁下載器:下載URL指定的網頁,存儲成字符串
  • 網頁解析器:提取有價值的數據,提取關聯URL補充URL管理器

二、URL管理器


三、網頁下載器


(1)方法一


(2)方法二

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

(3)方法三

  • HTTPCookieProcessor:需登錄的網頁
  • ProxyHandler:需代理訪問的網頁
  • HTTPSHandler:加密訪問的網頁
  • HTTPRedirectHandler:URL自動跳轉的網頁
# coding:utf8   #出現編碼錯誤時添加

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()

四、網頁解析器


Python 自帶:html.parser
第三方:BeautifulSouplxml

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



calss 為Python的關鍵詞,所以用class_表示。

以字典形式可訪問節點所有屬性

參考: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()

結果:
獲取所有的鏈接
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+MCtrl+Shift+oCtrl+1可以自動導入相應的包或創建相應的類或方法。

五、實例


觀察目標,定制策略,策略要根據目標的變化實時更新。

精通Python網絡爬蟲(Python3.X版本,PyCharm工具)

一、爬蟲類型

  • 通用網絡爬蟲:全網爬蟲。由初始URL集合、URL隊列、頁面爬行模塊、頁面分析模塊、頁面數據庫、鏈接過濾模塊等構成。
  • 聚焦網絡爬蟲:主題爬蟲。由初始URL集合、URL隊列、頁面爬行模塊、頁面分析模塊、頁面數據庫、鏈接過濾模塊,內容評價模塊、鏈接評價模塊等構成。
  • 增量網絡爬蟲:增量式更新,盡可能爬取新頁面(更新改變的部分)。
  • 深層網絡爬蟲:隱藏在表單后,需要提交一定關鍵詞才能獲取的頁面。URL列表、LVS列表(LVS指標簽/數值集合,即填充表單的數據源)、爬行控制器、解析器、LVS控制器、表單分析器、表單處理器、響應分析器等構成。

二、核心技術

PyCharm常用快捷鍵:
Alt+Enter:快速導入包
Ctrl+z:撤銷,Ctrl+Shift+z:反撤銷

(1)Urllib庫

1)Python2.X與Python3.X區別

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)快速爬取網頁

import urllib.request

# 爬取百度網頁內容
file = urllib.request.urlopen("http://www.baidu.com", timeout=30) # timeout超時設置,單位:秒
data = file.read()            #讀取文件全部內容,字符串類型
dataline = file.readline()    #讀取文件一行內容
datalines = file.readlines()  #讀取文件全部內容,列表類型

# 以html格式存儲到本地
fhandle = open("/.../1.html","wb")
fhandle.write(data)
fhandle.close()

# 快捷存儲到本地
filename = urllib.request.urlretrieve("http://www.baidu.com",filename="/.../1.html")
urllib.request.urlcleanup() #清除緩存

# 其他常用方法
file.getcode() #響應狀態碼,200為鏈接成功
file.geturl() #爬取的源網頁

# URL編碼(當URL中存在漢字等不符合標準的字符時需要編碼后爬取)
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時實際傳入URL應該是"https://www.baidu.com/s?wd=" + urllib.request.quote("電影"),而不應該是urllib.request.quote("https://www.baidu.com/s?wd=電影")

3)瀏覽器模擬(應對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請求

import urllib.request
import urllib.parse

url = "http://www.iqianyue.com/mypost"   # 測試網站
# 將數據使用urlencode編碼處理后,使用encode()設置為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")編碼后才可使用,實際結果為b'name=abc&pass=111',未編碼結果為name=abc&pass=111

5)代理服務器設置(應對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)
    # 創建全局默認opener對象,這樣使用urlopen()也會使用該對象
    urllib.request.install_opener(opener)
    # 解碼類型與網頁編碼格式一致
    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') # 結果:b'\xe4\xb8\xad\xe6\x96\x87',為字節類型
    u1 = str.decode('utf-8') # 結果:中文
  • 過程:
    str(unicode) --[encode('utf-8')]--> bytes --[decode('utf-8')]--> str(unicode)

6)DebugLog調試日志

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")

運行結果:

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)連接不上服務器。2)遠程URL不存在。3)無網絡。4)HTTPError:
200:OK
301:Moved Permanently——重定向到新的URL
302:Found——重定向到臨時的URL
304:Not Modified——請求資源未更新
400:Bad Request——非法請求
401:Unauthorized——請求未經授權
403:Forbidden——禁止訪問
404:Not Found——未找到對應頁面
500:Internal Server Error——服務器內部錯誤
501:Not Implemented——服務器不支持實現請求所需要的功能

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)正則表達式

1)基本語法(適用其他)

  • 1.單個字符匹配



[...]匹配字符集內的任意字符
\w包括[a-zA-Z0-9]即匹配所以大小寫字符及數字,以及下劃線

  • 2.多個字符匹配


因為*匹配前個字符0到無限次,所以*?匹配前個字符0次,既不匹配前個字符。
因為+匹配前個字符1到無限次,所以+?匹配前個字符1次。
因為?匹配前個字符0或1次,所以??匹配前個字符0次,既不匹配前個字符。

  • 3.邊界匹配


  • 4.分組匹配


\<num>引用編號為num的分組匹配的字符串:

代碼1:re.match(r'<(book>)(python)</\1\2','<book>python</book>python').group()
結果:<book>python</book>python
代碼2:re.match(r'<(book>)(python)</\1\2','<book>python</book>python').groups()
結果:('book>','python')

解釋:.groups()方法返回分組匹配的字符串集合,指總體匹配模式中()內的分組匹配模式匹配的結果集。代碼1中'<(book>)(python)</\1\2'為總體匹配模式,其中有(book>)(python)兩個分組匹配模式,代碼2結果就為這兩個分組匹配模式匹配的結果集,\<num>就是通過num來引用該結果集中的字符串,\1book>,\2python

(?P<name>)(?P=name)替代,代碼1還可以寫為:
re.match(r'<(?P<mark1>book>)(?P<mark2>python)</(?P=mark1)(?P=mark2)','<book>python</book>python').group()

  • 5.模式修改
符號 含義
I 匹配時忽略大小寫
M 多行匹配
L 做本地化識別匹配
U 根據Unicode字符及解析字符
S .匹配包括換行符,使.可以匹配任意字符

2)re模塊

import re
str = ‘imooc python’

pa = re.compile(r'imooc') #匹配‘imooc’字符串
ma = pa.match(str)
# 等價于
ma = re.match(r'imooc', str)

ma.string   #被匹配字符串
ma.re       #匹配模式(pa值)
ma.group()  #匹配結果
ma.span()   #匹配位置

pa = re.compile(r'imooc', re.I) #匹配‘imooc’字符串,不管大小寫

# 上述最終可寫為
ma = re.match(r'imooc', 'imooc python', re.I)


樣式字符串前r的用法:
(1)帶上r,樣式字符串為原字符串,后面的樣式字符串是什么匹配什么,里面即使有轉義字符串也按普通字符串匹配。
(2)不帶r,樣式字符串無轉義字符串不影響,有轉義字符串需考慮轉義字符串進行匹配。
例子中r'imooc\\n'相當于imooc\\n'imooc\\n'相當于imooc\n,因為'\\'為轉義字符串時相當于'\'

march從頭開始匹配,找出字符串開頭符合匹配樣式的部分,開頭無符合返回NoneType
seach從頭開始匹配,找出字符串內第一個符合匹配樣式的部分并返回,字符串內無符合返回NoneType


sub()參數中repl可以是用來替代的字符串,也可以是一個函數且該函數需返回一個用來替換的字符串。count為替換次數,默認為0,為都替換。
re.sub(r'\d+','100','imooc videnum=99')
re.sub(r'\d+',lambda x: str(int(x.group())+1),'imooc videnum=99')
結果:'imooc videnum=100'
lambda x: str(int(x.group())+1)為匿名函數,其中冒號前的x為函數參數,默認傳入匹配的結果對象,需要用.group()方法獲取結果字符串。冒號后算式的結果為返回值。也可以寫成:

def add(x):
    val = x.group()
    num = int(val)+1
    return str(num)

re.sub(r'\d+',add,'imooc videnum=99')

(3)Cookie用法(應對模擬登陸)

import urllib.request
import urllib.parse
import http.cookiejar

# 創建CookieJar對象
cjar = http.cookiejar.CookieJar()
# 使用HTTPCookieProcessor創建cookie處理器,并以其為參數創建opener對象
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cjar))
# 將opener安裝為全局
urllib.request.install_opener(opener)

# 網站登錄頁
url1 = 'http://xxx.com/index.php/login/login_new/'
# 登陸所需要POST的數據
postdata = urllib.parse.urlencode({
            'username': 'xxx',
            'password': 'xxx'
            }).encode("utf-8")
req = urllib.request.Request(url1, postdata)
# 網站登陸后才能訪問的網頁
url2 = 'http://xxx.com/index.php/myclass'

# 登陸網站
file1 = urllib.request.urlopen(req)
# 爬取目標網頁信息
file2 = urllib.request.urlopen(url2).read()

(4)多線程與隊列

# 多線程基礎
import threading

class A(threading.Thread):
    def __init__(self):
        # 初始化該線程
        threading.Thread.__init__(self)

    def run(self):
        # 該線程要執行的內容
        for i in range(10):
            print("線程A運行")

class B(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        for i in range(10):
            print("線程B運行")

t1 = A()
t1.start()

t2 = B()
t2.start()
# 隊列基礎(先進先出)
import queue
# 創建隊列對象
a = queue.Queue()
# 數據傳入隊列
a.put("hello")
a.put("php")
a.put("python")
a.put("bye")
# 結束數據傳入
a.task_done()

for i in range(4):
    # 取出數據
    print(a.get())

(5)瀏覽器偽裝

Headers信息:

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

  • Accept:瀏覽器支持內容類型,支持優先順序從左往右依次排序
  • text/html:HTML文檔
  • application/xhtml+xml:XHTML文檔
  • application/xml:XML文檔

Accept-Encoding:gzip, deflate, sdch(設置該字段,從服務器返回的是對應形式的壓縮代碼(瀏覽器會自動解壓縮),因此可能出現亂碼)

  • Accept-Encoding:瀏覽器支持的壓縮編碼方式
  • deflate:一種無損數據壓縮的算法

Accept-Language:zh-CN,zh;q=0.8

  • Accept-Language:支持的語言類型
  • zh-CNzh中文,CN簡體
  • en-US:英語(美國)

Connection:keep-alive

  • Connection:客戶端與服務端連接類型
  • keep-alive:持久性連接
  • close:連接斷開

Referer:http://123.sogou.com/(某些反爬蟲網址可能檢驗該字段,一般可以設置為要爬取網頁的域名地址或對應網址的主頁地址)

  • Referer:來源網址

·.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框架:有網頁版
  • newspaper框架
  • python-goose框架

(2)安裝Scrapy

  • Python2.X和Python3.X同時安裝,命令提示符:
    py -2:啟動Python2.X
    py -3:啟動Python3.X
    py -2 -m pip install ...:使用Python2.X pip安裝
    py -3 -m pip install ...:使用Python3.X pip安裝

  • 安裝超時:
    手動指定源,在pip后面跟-i,命令如下:
    pip install packagename -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
    pipy國內鏡像目前有:
    豆瓣 http://pypi.douban.com/simple/
    阿里云 http://mirrors.aliyun.com/pypi/simple/
    中國科技大學 https://pypi.mirrors.ustc.edu.cn/simple/
    清華大學 https://pypi.tuna.tsinghua.edu.cn/simple/
    華中理工大學 http://pypi.hustunique.com/
    山東理工大學 http://pypi.sdutlinux.org/

  • 出現如下錯誤:
    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對應版本的whl文件,cp后面是python版本,amd64代表64位,以Python位數為準
    運行命令:
    pip install C:\xxx\Twisted-17.5.0-cp36-cp36m-win_amd64.whl

  • 安裝成功后運行出現No module named 'win32api'錯誤:
    https://sourceforge.net/projects/pywin32/files%2Fpywin32/下載安裝對應pywin32即可

(3)Scrapy應用(命令提示符輸入)

1)創建項目

scrapy startproject myscrapy:創建名為myscrapy的爬蟲項目,自動生成如下目錄



目錄結構:

  • myscrapy/scrapy.cfg:爬蟲項目配置文件
  • myscrapy/myscrapy/items.py:數據容器文件,定義獲取的數據
  • myscrapy/myscrapy/pipelines.py:管道文件,對items定義的數據進行加工處理
  • myscrapy/myscrapy/settings.py:設置文件
  • myscrapy/myscrapy/spiders:放置爬蟲文件
  • myscrapy/myscrapy/middleware.py:下載中間件文件

參數控制:

  • scrapy startproject --logfile="../logf.log" myscrapy
    在創建myscrapy爬蟲項目同時在指定地址創建名為logf的日志文件
  • scrapy startproject --loglevel=DEBUG myscrapy
    創建項目同時指定日志信息的等級為DEBUG模式(默認),等級表如下:
等級名 含義
CRITICAL 發生最嚴重的錯誤
ERROR 發生必須立即處理的錯誤
WARNING 出現警告信息,存在潛在錯誤
INFO 輸出提示信息
DEBUG 輸出調試信息,常用于開發階段
  • scrapy startproject --nolog myscrapy
    創建項目同時指定不輸出日志

2)常用工具命令

全局命令:(項目文件夾外scrapy -h

  • scrapy fetch http://www.baidu.com:顯示爬取網站的過程
  • scrapy fetch --headers --nolog http://www.baidu.com:顯示頭信息不顯示日志信息
  • scrapy runspider 爬蟲文件.py -o xxx/xxx.xxx:運行指定爬蟲文件并將爬取結果存儲在指定文件內
  • scrapy setting --get BOT_NAME:項目內執行為項目名,項目外執行為scrapybot
  • scrapy shell http://www.baidu.com --nolog:爬取百度首頁創建一個交互終端環境并設置為不輸出日志信息。

項目命令:(項目文件夾內scrapy -h

  • scrapy bench:測試本地硬件性能
  • scrapy genspider -l:查看可使用的爬蟲模板
  • scrapy genspider -d 模板名:查看爬蟲模板內容
  • scrapy genspider -t 模板名 爬蟲名 要爬取的網站域名:快速創建一個爬蟲文件
  • scrapy check 爬蟲名:對爬蟲文件進行合同測試
  • scrapy crawl 爬蟲名:啟動爬蟲
  • scrapy list:顯示可以使用的爬蟲文件
  • scrapy edit 爬蟲名:編輯爬蟲文件(Windows下執行有問題)
  • scrapy parse 網站URL:獲取指定URL網站內容,并使用對應爬蟲文件處理分析,可設置的常用參數如下:
參數 含義
--spider==SPIDER 指定某個爬蟲文件進行處理
-a NAME=VALUE 設置爬蟲文件參數
--pipelines 通過pipelines處理items
--nolinks 不展示提取到的鏈接信息
--noitems 不展示得到的items
--nocolour 輸出結果顏色不高亮
--rules,-r 使用CrawlSpider規則處理回調函數
--callback=CALLBACK,-c CALLBACK 指定spider中用于處理返回的響應的回調函數
--depth=DEPTH,-d DEPTH 設置爬取深度,默認為1
--verbose,-v 顯示每層的詳細信息

3)Items編寫

import scrapy
class MyscrapyItem(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()
    ...

格式:數據名 = scrapy.Field()
實例化:item = MyscrapyItem(name = "xxx",...)
調用: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:允許爬取的域名,當開啟OffsiteMiddleware時,非允許的域名對應的網址會自動過濾,不再跟進。
  • start_urls:爬取的起始網址,如果沒有指定爬取的URL網址,則從該屬性中定義的網址開始進行爬取,可指定多個起始網址,網址間用逗號隔開。
  • parse方法:如果沒有特別指定回調函數,該方法是處理Scrapy爬蟲爬行到的網頁響應(response)的默認方法,通過該方法,可以對響應進行處理并返回處理后的數據,同時該方法也負責鏈接的跟進。
其他方法 含義
start_requests() 該方法默認讀取start_urls屬性中定義的網址(也可自定義),為每個網址生成一個Request請求對象,并返回可迭代對象
make_requests_from_url(url) 該方法會被start_requests() 調用,負責實現生成Request請求對象
close(reason) 關閉Spider時調用
log(message[,level, component]) 實現在Spider中添加log
__init__() 負責爬蟲初始化的構造函數
# -*- 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,不重寫時默認從start_urls中讀取起始網址
    def start_requests(self):
        for url in self.my_urls:
            # 調用默認make_requests_from_url()生成具體請求并迭代返回
            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基礎

  • /:選擇某個標簽,可多層標簽查找
  • //:提取某個標簽的所有信息
  • test():獲取該標簽的文本信息
  • //Z[@X="Y"]:獲取所有屬性X的值是Y的<Z>標簽的內容
  1. 返回一個SelectorList 對象
  2. 返回一個list、里面是一些提取的內容
  3. 返回2中list的第一個元素(如果list為空拋出異常)
  4. 返回1中SelectorList里的第一個元素(如果list為空拋出異常),和3達成的效果一致
  5. 4返回的是一個str, 所以5會返回str的第一個字符

6)Spider類參數傳遞(通過-a選項實現參數的傳遞)

# -*- 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/']
    # 重寫初始化方法,并設置參數myurl
    def __init__(self, myurl=None, *args, **kwargs):
        super(MyspiderSpider, self).__init__(*args, **kwargs)
        myurllist = myurl.split(",")
        # 輸出要爬的網站
        for i in myurllist:
            print("爬取網站:%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:設置迭代器,默認iternodes(基于正則表達式的高性能迭代器),此外還有htmlxml
  • itertag:設置開始迭代的節點
  • parse_node(self, response, selector):在節點與所提供的標簽名相符合的時候被調用,可進行信息的提取和處理操作
其他屬性或方法 含義
namespaces 以列表形式存在,主要定義在文檔中會被爬蟲處理的可用命名空間
adapt_response(response) 主要在spider分析響應(Response)前被調用
process_results(response, results) 主要在spider返回結果時被調用,對結果在返回前進行最后處理

8)CSVFeedSpider

CSV:一種簡單、通用的文件格式,其存儲的數據可以與表格數據相互轉化。最原始的形式是純文本形式,列之間通過,間隔,行之間通過換行間隔。

# -*- 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對象,并進行相應處理

9)CrawlSpider(自動爬取)

class MycrawlSpider(CrawlSpider):
    name = 'mycrawl'
    allowed_domains = ['sohu.com']
    start_urls = ['http://sohu.com/']
    # 自動爬取規則
    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:設置自動爬取規則,規則Rule的參數如下:
  • LinkExtractor:鏈接提取器,用來提取頁面中滿足條件的鏈接,以供下次爬取使用,可設置的參數如下
參數名 含義
allow 提取符合對應正則表達式的鏈接
deny 不提取符合對應正則表達式的鏈接
restrict_xpaths 使用XPath表達式與allow共同作用,提取出同時符合兩者的鏈接
allow_domains 允許提取的域名,該域名下的鏈接才可使用
deny_domains 禁止提取的域名,限制不提取該域名下的鏈接
  • callback='parse_item':處理的回調方法
  • follow=True:是否跟進。CrawlSpider爬蟲會根據鏈接提取器中設置的規則自動提取符合條件的網頁鏈接,提取之后再自動的對這些鏈接進行爬取,形成循環,如果鏈接設置為跟進,則會一直循環下去,如果設置為不跟進,則第一次循環后就會斷開。

10)避免爬蟲被禁止(settings.py內設置)

  • 禁止Cookie:(應對通過用戶Cookie信息對用戶識別和分析的網站)
# Disable cookies (enabled by default)
COOKIES_ENABLED = False
  • 設置下載延時:(設置爬取的時間間隔,應對通過網頁訪問(爬取)頻率進行分析的網站)
# 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池:(應對檢驗用戶IP的網站)

middlewares.py中或新創建一個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()方法,主要進行請求處理
    def process_request(self, request, spider):
        # 隨機選擇一個IP
        thisip = random.choice(self.myIPPOOL)
        # 將IP添加為具體代理,用該IP進行爬取
        request.meta["proxy"] = "http://" + thisip
        # 輸出觀察
        print('當前使用IP:%s' % request.meta["proxy"])

設置為默認下載中間件:

# Enable or disable downloader middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
    'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 123,
    # 格式:'下載中間件所在目錄.下載中間件文件名.下載中間件內部要使用的類':數字(有規定)
    'myscrapy.middlewares.IPPOOLS': 125
}
  • 用戶代理池

middlewares.py中或新創建一個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("當前使用UA: %s" % thisUA)

設置為默認下載中間件:

# 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引擎:框架核心,控制整個數據處理流程,以及觸發一些事務處理。
  • 調度器:存儲待爬取的網址,并確定網址優先級,同時會過濾一些重復的網址。
  • 下載器:對網頁資源進行高速下載,然后將這些數據傳遞給Scrapy引擎,再由引擎傳遞給爬蟲進行處理。
  • 下載中間件:下載器與引擎間的特殊組件,處理其之間的通信。
  • 爬蟲:接收并分析處理引擎的Response響應,提取所需數據。
  • 爬蟲中間件:爬蟲與引擎間的特殊組件,處理其之間的通信。
  • 實體管道:接收爬蟲組件中提取的數據,如:清洗、驗證、存儲至數據庫等

(5)Scrapy輸出與存儲

1)中文存儲

setting.py設置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):
        # 以寫入的方式創建或打開要存入數據的文件
        self.file = codecs.open('E:/xxx/mydata.txt',
                                'wb',
                                encoding="utf-8")
    # 主要處理方法,默認自動調用
    def process_item(self, item, spider):
        content = str(item) + '\n'
        self.file.write(content)
        return item
    # 關閉爬蟲時調用
    def close_spider(self, spider):
        self.file.close()

注意:要想執行process_item(),爬蟲文件parse()方法中必須返回item:yield item

2)Json輸出

import codecs
import json
class MyscrapyPipeline(object):
    def __init__(self):
        print("創建pip")
        # 以寫入的方式創建或打開要存入數據的文件
        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

    # 關閉爬蟲時調用
    def close_spider(self, spider):
        self.file.close()

注意:

  • 爬蟲文件parse()方法中,由response.xpath("xxx/text()")返回的SelectorList 對象不能轉換為Json類型,需要response.xpath("xxx/text()").extract()轉化為字符串列表類型才可轉化為Json類型。
  • json.dumps(dict(item), ensure_ascii=False):進行json.dumps()序列化時,中文信息默認使用ASCII編碼,當設定不使用ASCII編碼時,中文信息就可以正常顯示

3)數據庫操作

  • 安裝:pip install pymysql3
  • 導入:import pymysql
  • 鏈接MySQL:
    conn = pymysql.connect(host="主機名", user="賬號", passwd="密碼"[, db="數據庫名"])
  • SQL語句執行:
    conn.query("SQL語句")
  • 查看表內容:
# cursor()創建游標
cs = conn.cursor()
# execute()執行對應select語句
cs.execute("select * from mytb")
# 遍歷
for i in cs:
    print("當前是第"+str(cs.rownumber)+"行")
    print(i[x])

四、Scrapy文檔實例

(1)循環爬取http://quotes.toscrape.com/網站

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)

循環爬取時,注意循環的下個網頁需在allowed_domains域名下,否則會被過濾,從而無法循環

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,565評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,115評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,577評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,514評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,234評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,621評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,641評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,822評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,380評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,128評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,319評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,879評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,548評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,970評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,229評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,048評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,285評論 2 376

推薦閱讀更多精彩內容