網絡爬蟲(又被稱爲網頁蜘蛛,網絡機器人,在FOAF社區中間,更常常的稱爲網頁追逐者),是一種按照必定的規則,自動地抓取萬維網信息的程序或者腳本。另一些不常使用的名字還有螞蟻、自動索引、模擬程序或者蠕蟲。html
Python標準庫中提供了:urllib、urllib二、httplib等模塊以供Http請求,可是,它的 API 太渣了。它是爲另外一個時代、另外一個互聯網所建立的。它須要巨量的工做,甚至包括各類方法覆蓋,來完成最簡單的任務。python
import urllib2 import json import cookielib def urllib2_request(url, method="GET", cookie="", headers={}, data=None): """ :param url: 要請求的url :param cookie: 請求方式,GET、POST、DELETE、PUT.. :param cookie: 要傳入的cookie,cookie= 'k1=v1;k1=v2' :param headers: 發送數據時攜帶的請求頭,headers = {'ContentType':'application/json; charset=UTF-8'} :param data: 要發送的數據GET方式須要傳入參數,data={'d1': 'v1'} :return: 返回元祖,響應的字符串內容 和 cookiejar對象 對於cookiejar對象,可使用for循環訪問: for item in cookiejar: print item.name,item.value """ if data: data = json.dumps(data) cookie_jar = cookielib.CookieJar() handler = urllib2.HTTPCookieProcessor(cookie_jar) opener = urllib2.build_opener(handler) opener.addheaders.append(['Cookie', 'k1=v1;k1=v2']) request = urllib2.Request(url=url, data=data, headers=headers) request.get_method = lambda: method response = opener.open(request) origin = response.read() return origin, cookie_jar # GET result = urllib2_request('http://127.0.0.1:8001/index/', method="GET") # POST result = urllib2_request('http://127.0.0.1:8001/index/', method="POST", data= {'k1': 'v1'}) # PUT result = urllib2_request('http://127.0.0.1:8001/index/', method="PUT", data= {'k1': 'v1'})
Requests 是使用 Apache2 Licensed 許可證的 基於Python開發的HTTP 庫,其在Python內置模塊的基礎上進行了高度的封裝,從而使得Pythoner進行網絡請求時,變得美好了許多,使用Requests能夠垂手可得的完成瀏覽器可有的任何操做。git
一、GET請求github
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 一、無參數實例
import
requests
ret
=
requests.get(
'https://github.com/timeline.json'
)
print
ret.url
print
ret.text
# 二、有參數實例
import
requests
payload
=
{
'key1'
:
'value1'
,
'key2'
:
'value2'
}
ret
=
requests.get(
"http://httpbin.org/get"
, params
=
payload)
print
ret.url
print
ret.text
|
向 https://github.com/timeline.json 發送一個GET請求,將請求和響應相關均封裝在 ret 對象中。ajax
二、POST請求正則表達式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
# 一、基本POST實例
import
requests
payload
=
{
'key1'
:
'value1'
,
'key2'
:
'value2'
}
ret
=
requests.post(
"http://httpbin.org/post"
, data
=
payload)
print
ret.text
# 二、發送請求頭和數據實例
import
requests
import
json
url
=
'https://api.github.com/some/endpoint'
payload
=
{
'some'
:
'data'
}
headers
=
{
'content-type'
:
'application/json'
}
ret
=
requests.post(url, data
=
json.dumps(payload), headers
=
headers)
print
ret.text
print
ret.cookies
|
向https://api.github.com/some/endpoint發送一個POST請求,將請求和相應相關的內容封裝在 ret 對象中。json
三、其餘請求windows
1
2
3
4
5
6
7
8
9
10
|
requests.get(url, params
=
None
,
*
*
kwargs)
requests.post(url, data
=
None
, json
=
None
,
*
*
kwargs)
requests.put(url, data
=
None
,
*
*
kwargs)
requests.head(url,
*
*
kwargs)
requests.delete(url,
*
*
kwargs)
requests.patch(url, data
=
None
,
*
*
kwargs)
requests.options(url,
*
*
kwargs)
# 以上方法均是在此方法的基礎上構建
requests.request(method, url,
*
*
kwargs)
|
requests模塊已經將經常使用的Http請求方法爲用戶封裝完成,用戶直接調用其提供的相應方法便可,其中方法的全部參數有:api
def request(method, url, **kwargs): """Constructs and sends a :class:`Request <Request>`. :param method: method for the new :class:`Request` object. :param url: URL for the new :class:`Request` object. :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. :param json: (optional) json data to send in the body of the :class:`Request`. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': ('filename', fileobj)}``) for multipart encoding upload. :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. :param timeout: (optional) How long to wait for the server to send data before giving up, as a float, or a :ref:`(connect timeout, read timeout) <timeouts>` tuple. :type timeout: float or tuple :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed. :type allow_redirects: bool :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. :param verify: (optional) whether the SSL cert will be verified. A CA_BUNDLE path can also be provided. Defaults to ``True``. :param stream: (optional) if ``False``, the response content will be immediately downloaded. :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. :return: :class:`Response <Response>` object :rtype: requests.Response Usage:: >>> import requests >>> req = requests.request('GET', 'http://httpbin.org/get') <Response [200]> """ # By using the 'with' statement we are sure the session is closed, thus we # avoid leaving sockets open which can trigger a ResourceWarning in some # cases, and look like a memory leak in others. with sessions.Session() as session: return session.request(method=method, url=url, **kwargs)
更多requests模塊相關的文檔見:http://cn.python-requests.org/zh_CN/latest/瀏覽器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
### 一、首先登錄任何頁面,獲取cookie
i1
=
requests.get(url
=
"http://dig.chouti.com/help/service"
)
### 二、用戶登錄,攜帶上一次的cookie,後臺對cookie中的 gpsd 進行受權
i2
=
requests.post(
url
=
"http://dig.chouti.com/login"
,
data
=
{
'phone'
:
"86手機號"
,
'password'
:
"密碼"
,
'oneMonth'
: ""
},
cookies
=
i1.cookies.get_dict()
)
### 三、點贊(只須要攜帶已經被受權的gpsd便可)
gpsd
=
i1.cookies.get_dict()[
'gpsd'
]
i3
=
requests.post(
url
=
"http://dig.chouti.com/link/vote?linksId=8589523"
,
cookies
=
{
'gpsd'
: gpsd}
)
print
(i3.text)
|
「破解」微信公衆號其實就是使用Python代碼自動實現【登錄公衆號】->【獲取觀衆用戶】-> 【向關注用戶發送消息】。
注:只能向48小時內有互動的粉絲主動推送消息
一、自動登錄
分析對於Web登錄頁面,用戶登錄驗證時僅作了以下操做:
{
'username': 用戶名,
'pwd': 密碼的MD5值,
'imgcode': "",
'f': 'json'
}
注:imgcode是須要提供的驗證碼,默認無需驗證碼,只有在屢次登錄未成功時,才須要用戶提供驗證碼才能登錄
# -*- coding:utf-8 -*- import requests import time import hashlib def _password(pwd): ha = hashlib.md5() ha.update(pwd) return ha.hexdigest() def login(): login_dict = { 'username': "用戶名", 'pwd': _password("密碼"), 'imgcode': "", 'f': 'json' } login_res = requests.post( url= "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN", data=login_dict, headers={'Referer': 'https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN'}) # 登錄成功以後獲取服務器響應的cookie resp_cookies_dict = login_res.cookies.get_dict() # 登錄成功後,獲取服務器響應的內容 resp_text = login_res.text # 登錄成功後,獲取token token = re.findall(".*token=(\d+)", resp_text)[0] print resp_text print token print resp_cookies_dict login()
登錄成功獲取的相應內容以下:
1
2
3
4
5
|
響應內容:
{
"base_resp"
:{
"ret"
:
0
,
"err_msg"
:
"ok"
},
"redirect_url"
:
"\/cgi-bin\/home?t=home\/index&lang=zh_CN&token=537908795"
}
響應cookie:
{
'data_bizuin'
:
'3016804678'
,
'bizuin'
:
'3016804678'
,
'data_ticket'
:
'CaoX+QA0ZA9LRZ4YM3zZkvedyCY8mZi0XlLonPwvBGkX0/jY/FZgmGTq6xGuQk4H'
,
'slave_user'
:
'gh_5abeaed48d10'
,
'slave_sid'
:
'elNLbU1TZHRPWDNXSWdNc2FjckUxalM0Y000amtTamlJOUliSnRnWGRCdjFseV9uQkl5cUpHYkxqaGJNcERtYnM2WjdFT1pQckNwMFNfUW5fUzVZZnFlWGpSRFlVRF9obThtZlBwYnRIVGt6cnNGbUJsNTNIdTlIc2JJU29QM2FPaHZjcTcya0F6UWRhQkhO'
}
|
二、訪問其餘頁面獲取用戶信息
分析用戶管理頁面,經過Pyhton代碼以Get方式訪問此頁面,分析響應到的 HTML 代碼,從中獲取用戶信息:
1
|
{
'data_bizuin'
:
'3016804678'
,
'bizuin'
:
'3016804678'
,
'data_ticket'
: 'C4YM3zZ...
|
# -*- coding:utf-8 -*- import requests import time import hashlib import json import re LOGIN_COOKIES_DICT = {} def _password(pwd): ha = hashlib.md5() ha.update(pwd) return ha.hexdigest() def login(): login_dict = { 'username': "用戶名", 'pwd': _password("密碼"), 'imgcode': "", 'f': 'json' } login_res = requests.post( url= "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN", data=login_dict, headers={'Referer': 'https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN'}) # 登錄成功以後獲取服務器響應的cookie resp_cookies_dict = login_res.cookies.get_dict() # 登錄成功後,獲取服務器響應的內容 resp_text = login_res.text # 登錄成功後,獲取token token = re.findall(".*token=(\d+)", resp_text)[0] return {'token': token, 'cookies': resp_cookies_dict} def standard_user_list(content): content = re.sub('\s*', '', content) content = re.sub('\n*', '', content) data = re.findall("""cgiData=(.*);seajs""", content)[0] data = data.strip() while True: temp = re.split('({)(\w+)(:)', data, 1) if len(temp) == 5: temp[2] = '"' + temp[2] + '"' data = ''.join(temp) else: break while True: temp = re.split('(,)(\w+)(:)', data, 1) if len(temp) == 5: temp[2] = '"' + temp[2] + '"' data = ''.join(temp) else: break data = re.sub('\*\d+', "", data) ret = json.loads(data) return ret def get_user_list(): login_dict = login() LOGIN_COOKIES_DICT.update(login_dict) login_cookie_dict = login_dict['cookies'] res_user_list = requests.get( url= "https://mp.weixin.qq.com/cgi-bin/user_tag", params = {"action": "get_all_data", "lang": "zh_CN", "token": login_dict['token']}, cookies = login_cookie_dict, headers={'Referer': 'https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN'} ) user_info = standard_user_list(res_user_list.text) for item in user_info['user_list']: print "%s %s " % (item['nick_name'],item['id'],) get_user_list()
三、發送消息
分析給用戶發送消息的頁面,從網絡請求中剖析獲得發送消息的URL,從而使用Python代碼發送消息:
1
2
3
4
5
6
7
8
9
10
11
|
send_dict
=
{
'token'
: 登錄時獲取的token,
'lang'
:
"zh_CN"
,
'f'
:
'json'
,
'ajax'
:
1
,
'random'
:
"0.5322618900912392"
,
'type'
:
1
,
'content'
: 要發送的內容,
'tofakeid'
: 用戶列表中獲取的用戶的
ID
,
'imgcode'
: ''
}
|
# -*- coding:utf-8 -*- import requests import time import hashlib import json import re LOGIN_COOKIES_DICT = {} def _password(pwd): ha = hashlib.md5() ha.update(pwd) return ha.hexdigest() def login(): login_dict = { 'username': "用戶名", 'pwd': _password("密碼"), 'imgcode': "", 'f': 'json' } login_res = requests.post( url= "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN", data=login_dict, headers={'Referer': 'https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN'}) # 登錄成功以後獲取服務器響應的cookie resp_cookies_dict = login_res.cookies.get_dict() # 登錄成功後,獲取服務器響應的內容 resp_text = login_res.text # 登錄成功後,獲取token token = re.findall(".*token=(\d+)", resp_text)[0] return {'token': token, 'cookies': resp_cookies_dict} def standard_user_list(content): content = re.sub('\s*', '', content) content = re.sub('\n*', '', content) data = re.findall("""cgiData=(.*);seajs""", content)[0] data = data.strip() while True: temp = re.split('({)(\w+)(:)', data, 1) if len(temp) == 5: temp[2] = '"' + temp[2] + '"' data = ''.join(temp) else: break while True: temp = re.split('(,)(\w+)(:)', data, 1) if len(temp) == 5: temp[2] = '"' + temp[2] + '"' data = ''.join(temp) else: break data = re.sub('\*\d+', "", data) ret = json.loads(data) return ret def get_user_list(): login_dict = login() LOGIN_COOKIES_DICT.update(login_dict) login_cookie_dict = login_dict['cookies'] res_user_list = requests.get( url= "https://mp.weixin.qq.com/cgi-bin/user_tag", params = {"action": "get_all_data", "lang": "zh_CN", "token": login_dict['token']}, cookies = login_cookie_dict, headers={'Referer': 'https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN'} ) user_info = standard_user_list(res_user_list.text) for item in user_info['user_list']: print "%s %s " % (item['nick_name'],item['id'],) def send_msg(user_fake_id, content='啥也沒發'): login_dict = LOGIN_COOKIES_DICT token = login_dict['token'] login_cookie_dict = login_dict['cookies'] send_dict = { 'token': token, 'lang': "zh_CN", 'f': 'json', 'ajax': 1, 'random': "0.5322618900912392", 'type': 1, 'content': content, 'tofakeid': user_fake_id, 'imgcode': '' } send_url = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response&f=json&token=%s&lang=zh_CN" % (token,) message_list = requests.post( url=send_url, data=send_dict, cookies=login_cookie_dict, headers={'Referer': 'https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN'} ) get_user_list() fake_id = raw_input('請輸入用戶ID:') content = raw_input('請輸入消息內容:') send_msg(fake_id, content)
以上就是「破解」微信公衆號的整個過程,經過Python代碼實現了自動【登錄微信公衆號平臺】【獲取用戶列表】【指定用戶發送消息】。
Scrapy是一個爲了爬取網站數據,提取結構性數據而編寫的應用框架。 其能夠應用在數據挖掘,信息處理或存儲歷史數據等一系列的程序中。
其最初是爲了頁面抓取 (更確切來講, 網絡抓取 )所設計的, 也能夠應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。Scrapy用途普遍,能夠用於數據挖掘、監測和自動化測試。
Scrapy 使用了 Twisted異步網絡庫來處理網絡通信。總體架構大體以下
Scrapy主要包括瞭如下組件:
Scrapy運行流程大概以下:
1、安裝
1
|
pip install Scrapy
|
注:windows平臺須要依賴pywin32,請根據本身系統32/64位選擇下載安裝,https://sourceforge.net/projects/pywin32/
2、基本使用
一、建立項目
運行命令:
1
|
scrapy startproject your_project_name
|
自動建立目錄:
1
2
3
4
5
6
7
8
9
|
project_name
/
scrapy.cfg
project_name
/
__init__.py
items.py
pipelines.py
settings.py
spiders
/
__init__.py
|
文件說明:
注意:通常建立爬蟲文件時,以網站域名命名
二、編寫爬蟲
在spiders目錄中新建 xiaohuar_spider.py 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import
scrapy
class
XiaoHuarSpider(scrapy.spiders.Spider):
name
=
"xiaohuar"
allowed_domains
=
[
"xiaohuar.com"
]
start_urls
=
[
"http://www.xiaohuar.com/hua/"
,
]
def
parse(
self
, response):
# print(response, type(response))
# from scrapy.http.response.html import HtmlResponse
# print(response.body_as_unicode())
current_url
=
response.url
body
=
response.body
unicode_body
=
response.body_as_unicode()
|
三、運行
進入project_name目錄,運行命令
1
|
scrapy crawl spider_name
-
-
nolog
|
四、遞歸的訪問
以上的爬蟲僅僅是爬去初始頁,而咱們爬蟲是須要源源不斷的執行下去,直到全部的網頁被執行完畢
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import
scrapy
from
scrapy.http
import
Request
from
scrapy.selector
import
HtmlXPathSelector
import
re
import
urllib
import
os
class
XiaoHuarSpider(scrapy.spiders.Spider):
name
=
"xiaohuar"
allowed_domains
=
[
"xiaohuar.com"
]
start_urls
=
[
"http://www.xiaohuar.com/list-1-1.html"
,
]
def
parse(
self
, response):
# 分析頁面
# 找到頁面中符合規則的內容(校花圖片),保存
# 找到全部的a標籤,再訪問其餘a標籤,一層一層的搞下去
hxs
=
HtmlXPathSelector(response)
# 若是url是 http://www.xiaohuar.com/list-1-\d+.html
if
re.match(
'http://www.xiaohuar.com/list-1-\d+.html'
, response.url):
items
=
hxs.select(
'//div[@class="item_list infinite_scroll"]/div'
)
for
i
in
range
(
len
(items)):
src
=
hxs.select(
'//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/a/img/@src'
%
i).extract()
name
=
hxs.select(
'//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/span/text()'
%
i).extract()
school
=
hxs.select(
'//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/div[@class="btns"]/a/text()'
%
i).extract()
if
src:
ab_src
=
"http://www.xiaohuar.com"
+
src[
0
]
file_name
=
"%s_%s.jpg"
%
(school[
0
].encode(
'utf-8'
), name[
0
].encode(
'utf-8'
))
file_path
=
os.path.join(
"/Users/wupeiqi/PycharmProjects/beauty/pic"
, file_name)
urllib.urlretrieve(ab_src, file_path)
# 獲取全部的url,繼續訪問,並在其中尋找相同的url
all_urls
=
hxs.select(
'//a/@href'
).extract()
for
url
in
all_urls:
if
url.startswith(
'http://www.xiaohuar.com/list-1-'
):
yield
Request(url, callback
=
self
.parse)
|
以上代碼將符合規則的頁面中的圖片保存在指定目錄,而且在HTML源碼中找到全部的其餘 a 標籤的href屬性,從而「遞歸」的執行下去,直到全部的頁面都被訪問過爲止。以上代碼之因此能夠進行「遞歸」的訪問相關URL,關鍵在於parse方法使用了 yield Request對象。
注:能夠修改settings.py 中的配置文件,以此來指定「遞歸」的層數,如: DEPTH_LIMIT = 1
from scrapy.selector import Selector from scrapy.http import HtmlResponse html = """<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <li class="item-"><a href="link.html">first item</a></li> <li class="item-0"><a href="link1.html">first item</a></li> <li class="item-1"><a href="link2.html">second item</a></li> </body> </html> """ response = HtmlResponse(url='http://example.com', body=html,encoding='utf-8') ret = Selector(response=response).xpath('//li[re:test(@class, "item-\d*")]//@href').extract() print(ret)
#!/usr/bin/env python # -*- coding:utf-8 -*- import scrapy import hashlib from tutorial.items import JinLuoSiItem from scrapy.http import Request from scrapy.selector import HtmlXPathSelector class JinLuoSiSpider(scrapy.spiders.Spider): count = 0 url_set = set() name = "jluosi" domain = 'http://www.jluosi.com' allowed_domains = ["jluosi.com"] start_urls = [ "http://www.jluosi.com:80/ec/goodsDetail.action?jls=QjRDNEIzMzAzOEZFNEE3NQ==", ] def parse(self, response): md5_obj = hashlib.md5() md5_obj.update(response.url) md5_url = md5_obj.hexdigest() if md5_url in JinLuoSiSpider.url_set: pass else: JinLuoSiSpider.url_set.add(md5_url) hxs = HtmlXPathSelector(response) if response.url.startswith('http://www.jluosi.com:80/ec/goodsDetail.action'): item = JinLuoSiItem() item['company'] = hxs.select('//div[@class="ShopAddress"]/ul/li[1]/text()').extract() item['link'] = hxs.select('//div[@class="ShopAddress"]/ul/li[2]/text()').extract() item['qq'] = hxs.select('//div[@class="ShopAddress"]//a/@href').re('.*uin=(?P<qq>\d*)&') item['address'] = hxs.select('//div[@class="ShopAddress"]/ul/li[4]/text()').extract() item['title'] = hxs.select('//h1[@class="goodsDetail_goodsName"]/text()').extract() item['unit'] = hxs.select('//table[@class="R_WebDetail_content_tab"]//tr[1]//td[3]/text()').extract() product_list = [] product_tr = hxs.select('//table[@class="R_WebDetail_content_tab"]//tr') for i in range(2,len(product_tr)): temp = { 'standard':hxs.select('//table[@class="R_WebDetail_content_tab"]//tr[%d]//td[2]/text()' %i).extract()[0].strip(), 'price':hxs.select('//table[@class="R_WebDetail_content_tab"]//tr[%d]//td[3]/text()' %i).extract()[0].strip(), } product_list.append(temp) item['product_list'] = product_list yield item current_page_urls = hxs.select('//a/@href').extract() for i in range(len(current_page_urls)): url = current_page_urls[i] if url.startswith('http://www.jluosi.com'): url_ab = url yield Request(url_ab, callback=self.parse)
def parse(self, response): from scrapy.http.cookies import CookieJar cookieJar = CookieJar() cookieJar.extract_cookies(response, response.request) print(cookieJar._cookies)
更多選擇器規則:http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/selectors.html
五、格式化處理
上述實例只是簡單的圖片處理,因此在parse方法中直接處理。若是對於想要獲取更多的數據(獲取頁面的價格、商品名稱、QQ等),則能夠利用Scrapy的items將數據格式化,而後統一交由pipelines來處理。
在items.py中建立類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html
import
scrapy
class
JieYiCaiItem(scrapy.Item):
company
=
scrapy.Field()
title
=
scrapy.Field()
qq
=
scrapy.Field()
info
=
scrapy.Field()
more
=
scrapy.Field()
|
上述定義模板,之後對於從請求的源碼中獲取的數據贊成按照此結構來獲取,因此在spider中須要有一下操做:
#!/usr/bin/env python # -*- coding:utf-8 -*- import scrapy import hashlib from beauty.items import JieYiCaiItem from scrapy.http import Request from scrapy.selector import HtmlXPathSelector from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class JieYiCaiSpider(scrapy.spiders.Spider): count = 0 url_set = set() name = "jieyicai" domain = 'http://www.jieyicai.com' allowed_domains = ["jieyicai.com"] start_urls = [ "http://www.jieyicai.com", ] rules = [ #下面是符合規則的網址,可是不抓取內容,只是提取該頁的連接(這裏網址是虛構的,實際使用時請替換) #Rule(SgmlLinkExtractor(allow=(r'http://test_url/test?page_index=\d+'))), #下面是符合規則的網址,提取內容,(這裏網址是虛構的,實際使用時請替換) #Rule(LinkExtractor(allow=(r'http://www.jieyicai.com/Product/Detail.aspx?pid=\d+')), callback="parse"), ] def parse(self, response): md5_obj = hashlib.md5() md5_obj.update(response.url) md5_url = md5_obj.hexdigest() if md5_url in JieYiCaiSpider.url_set: pass else: JieYiCaiSpider.url_set.add(md5_url) hxs = HtmlXPathSelector(response) if response.url.startswith('http://www.jieyicai.com/Product/Detail.aspx'): item = JieYiCaiItem() item['company'] = hxs.select('//span[@class="username g-fs-14"]/text()').extract() item['qq'] = hxs.select('//span[@class="g-left bor1qq"]/a/@href').re('.*uin=(?P<qq>\d*)&') item['info'] = hxs.select('//div[@class="padd20 bor1 comard"]/text()').extract() item['more'] = hxs.select('//li[@class="style4"]/a/@href').extract() item['title'] = hxs.select('//div[@class="g-left prodetail-text"]/h2/text()').extract() yield item current_page_urls = hxs.select('//a/@href').extract() for i in range(len(current_page_urls)): url = current_page_urls[i] if url.startswith('/'): url_ab = JieYiCaiSpider.domain + url yield Request(url_ab, callback=self.parse)
此處代碼的關鍵在於:
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html import json from twisted.enterprise import adbapi import MySQLdb.cursors import re mobile_re = re.compile(r'(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}') phone_re = re.compile(r'(\d+-\d+|\d+)') class JsonPipeline(object): def __init__(self): self.file = open('/Users/wupeiqi/PycharmProjects/beauty/beauty/jieyicai.json', 'wb') def process_item(self, item, spider): line = "%s %s\n" % (item['company'][0].encode('utf-8'), item['title'][0].encode('utf-8')) self.file.write(line) return item class DBPipeline(object): def __init__(self): self.db_pool = adbapi.ConnectionPool('MySQLdb', db='DbCenter', user='root', passwd='123', cursorclass=MySQLdb.cursors.DictCursor, use_unicode=True) def process_item(self, item, spider): query = self.db_pool.runInteraction(self._conditional_insert, item) query.addErrback(self.handle_error) return item def _conditional_insert(self, tx, item): tx.execute("select nid from company where company = %s", (item['company'][0], )) result = tx.fetchone() if result: pass else: phone_obj = phone_re.search(item['info'][0].strip()) phone = phone_obj.group() if phone_obj else ' ' mobile_obj = mobile_re.search(item['info'][1].strip()) mobile = mobile_obj.group() if mobile_obj else ' ' values = ( item['company'][0], item['qq'][0], phone, mobile, item['info'][2].strip(), item['more'][0]) tx.execute("insert into company(company,qq,phone,mobile,address,more) values(%s,%s,%s,%s,%s,%s)", values) def handle_error(self, e): print 'error',e
上述中的pipelines中有多個類,到底Scapy會自動執行那個?哈哈哈哈,固然須要先配置了,否則Scapy就蒙逼了。。。
在settings.py中作以下配置:
1
2
3
4
5
|
ITEM_PIPELINES
=
{
'beauty.pipelines.DBPipeline'
:
300
,
'beauty.pipelines.JsonPipeline'
:
100
,
}
# 每行後面的整型值,肯定了他們運行的順序,item按數字從低到高的順序,經過pipeline,一般將這些數字定義在0-1000範圍內。
|
更多請參見Scrapy文檔:http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html