感覺(jué)距離上次交作業(yè)已經(jīng)很久了...
已經(jīng)落后大家這么多了
思路分析
上次交的作業(yè)雖然能夠爬取到數(shù)據(jù)脖阵,但是和大家的不同力穗,完全偏離主題呀,心好痛
這次作業(yè)的關(guān)鍵點(diǎn)是構(gòu)造表單蔫仙,附帶cookies發(fā)送請(qǐng)求
具體的思路前面的同學(xué)已經(jīng)分析的很詳細(xì)了哀卫,就不作一一的論述了
所以這次作業(yè)的主題是總結(jié)與探討兩個(gè)問(wèn)題
為什么帶cookies可以反ban?
想要解決這個(gè)問(wèn)題,就要弄清楚幾個(gè)概念
cookies(來(lái)自維基百科)
因?yàn)?a target="_blank" rel="nofollow">HTTP協(xié)議是無(wú)狀態(tài)的撬槽,即服務(wù)器不知道用戶上一次做了什么此改,這嚴(yán)重阻礙了交互式Web應(yīng)用程序的實(shí)現(xiàn)。在典型的網(wǎng)上購(gòu)物場(chǎng)景中侄柔,用戶瀏覽了幾個(gè)頁(yè)面共啃,買(mǎi)了一盒餅干和兩飲料。最后結(jié)帳時(shí)暂题,由于HTTP的無(wú)狀態(tài)性移剪,不通過(guò)額外的手段,服務(wù)器并不知道用戶到底買(mǎi)了什么薪者。 所以Cookie就是用來(lái)繞開(kāi)HTTP的無(wú)狀態(tài)性的“額外手段”之一纵苛。服務(wù)器可以設(shè)置或讀取Cookies中包含信息,借此維護(hù)用戶跟服務(wù)器會(huì)話中的狀態(tài)。
從cookies的定義可以看出攻人,cookies也是可以作為一個(gè)驗(yàn)證用戶身份的工具取试,所以可以通過(guò)cookies來(lái)區(qū)別機(jī)器和人,所以有一種反爬的策略怀吻,就是通過(guò)cookies瞬浓,拉勾網(wǎng)的反爬蟲(chóng)機(jī)制就是基于cookies,所以同一個(gè)cookies可以重復(fù)請(qǐng)求蓬坡,而同一個(gè)IP不帶cookies卻是不能重復(fù)請(qǐng)求猿棉,會(huì)封IP,但是拉勾網(wǎng)做的還是不夠屑咳,就是只需要一個(gè)cookies萨赁,如果再?lài)?yán)格一點(diǎn),使cookies所保存的時(shí)間短一點(diǎn)乔宿,也許就需要一個(gè)cookies池了位迂,定期加入cookies,這樣爬取的難度就會(huì)增大很多详瑞,再厲害一點(diǎn)掂林,就是IP和cookes一起識(shí)別。
總結(jié)一下目前所遇到的爬取方式
目前所爬取的網(wǎng)站只有三個(gè)坝橡,還是比較少的
但是我覺(jué)得都挺有收獲和代表性的
以源碼顯示網(wǎng)站
這個(gè)基本的是有多基本呢泻帮?只需要正常的提交請(qǐng)求,就能夠得到網(wǎng)站的源碼计寇,然后選取自己想要的信息锣杂,比如說(shuō)有些教務(wù)網(wǎng)和一些小型的網(wǎng)站
以json等格式顯示的網(wǎng)站
如簡(jiǎn)書(shū)的專(zhuān)題數(shù)據(jù),這類(lèi)的數(shù)據(jù)番宁,我們所獲取的內(nèi)容不是網(wǎng)站的源碼元莫,所獲取的是其返回的json等格式的包,所以獲取信息的關(guān)鍵在于解析這個(gè)包來(lái)得到自己想要的內(nèi)容
Ajax形式網(wǎng)站
首先來(lái)個(gè)科普
維基百科
傳統(tǒng)的Web應(yīng)用允許用戶端填寫(xiě)表單(form)蝶押,當(dāng)提交表單時(shí)就向網(wǎng)頁(yè)服務(wù)器發(fā)送一個(gè)請(qǐng)求踱蠢。服務(wù)器接收并處理傳來(lái)的表單,然后送回一個(gè)新的網(wǎng)頁(yè)棋电,但這個(gè)做法浪費(fèi)了許多帶寬茎截,因?yàn)樵谇昂髢蓚€(gè)頁(yè)面中的大部分HTML碼往往是相同的。由于每次應(yīng)用的溝通都需要向服務(wù)器發(fā)送請(qǐng)求赶盔,應(yīng)用的回應(yīng)時(shí)間依賴(lài)于服務(wù)器的回應(yīng)時(shí)間企锌。這導(dǎo)致了用戶界面的回應(yīng)比本機(jī)應(yīng)用慢得多。
與此不同于未,AJAX應(yīng)用可以僅向服務(wù)器發(fā)送并取回必須的數(shù)據(jù)撕攒,并在客戶端采用JavaScript處理來(lái)自服務(wù)器的回應(yīng)陡鹃。因?yàn)樵诜?wù)器和瀏覽器之間交換的數(shù)據(jù)大量減少(大約只有原來(lái)的5%)
這一類(lèi)的網(wǎng)站有如拉勾網(wǎng),這一類(lèi)網(wǎng)站的特點(diǎn)和上一種有點(diǎn)類(lèi)似打却,但是為什么要分開(kāi)來(lái)說(shuō)呢杉适?因?yàn)樯弦环N情形一般會(huì)和源碼一起結(jié)合出現(xiàn),而Ajax網(wǎng)站柳击,需要用戶構(gòu)造表單猿推,向服務(wù)器要求返回想要的內(nèi)容,而且這一類(lèi)網(wǎng)站捌肴,一般是幾乎所有信息(除了導(dǎo)航欄之類(lèi)的通用信息)外蹬叭,都是通過(guò)json, xml返回的,所以只需要從這個(gè)json包中便能獲取所有數(shù)據(jù)状知。
作業(yè)代碼
spider.py
# -*- coding: utf-8 -*-
import scrapy
from lagou.items import LagouItem
import sys
import re
import requests
import json
from bs4 import BeautifulSoup
#import sys
#reload(sys)
#sys.setdefaultencoding('utf-8')
class LagouSpider(scrapy.Spider):
name = "lagou"
cookies = {
'user_trace_token': '20170314211704-f55f18938db84cfeae95d1efec6d585e',
'LGUID': '20170314211706-859943f0-08b8-11e7-93e0-5254005c3644',
'JSESSIONID': 'AA1DE67564F4C20F86F89F3572B706A1',
'PRE_UTM': '',
'PRE_HOST': 'www.baidu.com',
'PRE_SITE': 'https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DuQkzN6ld65B8UHLJeaN2RVwWb3jiAl6AkSQSZRkXpRC%26wd%3D%26eqid%3Df6aa96cc0000dd5e0000000258ff3f34',
'PRE_LAND': 'https%3A%2F%2Fwww.lagou.com%2F',
'index_location_city': '%E5%85%A8%E5%9B%BD',
'Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6': '1491116405,1491116452,1493122880,1493122898',
'Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6': '1493123186',
'_ga': 'GA1.2.1412866745.1489497427',
'LGSID': '20170425202132-b7ea71dc-29b1-11e7-bc70-525400f775ce',
'LGRID': '20170425202620-6394f6bd-29b2-11e7-bc72-525400f775ce',
'TG-TRACK-CODE': 'search_code',
'SEARCH_ID': '63e7755cfbbf40559a5dac6a35e5f49f'
}
def start_requests(self):
kds = ['python工程師', 'python數(shù)據(jù)分析']
citys = ['北京', '上海', '深圳', '廣州', '杭州', '成都', '南京', '武漢', '西安', '廈門(mén)', '長(zhǎng)沙', '蘇州', '天津']
#soup = BeautifulSoup(response.text, 'lxml')
#pages = soup.find('span', {'class': 'span totalNum'}).get_text()
base_url = "https://www.lagou.com/jobs/positionAjax.json?city="
for city in citys:
city_urls = base_url + city + "&needAddtionalResult=false"
for kd in kds:
url = "https://www.lagou.com/jobs/list_{}?px=default&city={}#filterBox".format(city, kd)
r = requests.get(url, cookies=self.cookies)
soup = BeautifulSoup(r.text, 'lxml')
pages = soup.find('span', {'class': 'span totalNum'}).get_text()
for i in range(1, int(pages)+1):
formdata = {"first":"ture", "pn": str(i), "kd": kd}
yield scrapy.FormRequest(city_urls, formdata=formdata, cookies=self.cookies, callback=self.parse)
def parse(self, response):
data = json.loads(response.text)
item = LagouItem()
da = data['content']
a = da['positionResult']
n = a['result']
for one in n:
city = one["city"]
companyname = one["companyFullName"]
#companysize = one["companySize"]
district = one["district"]
education = one["education"]
jobNature = one["jobNature"]
try:
positionLables = ""
Lables = one["positionLables"]
for i in Lables:
positionLables += i
item["positionLables"] = positionLables
except:
item["positionLables"] = u""
try:
positionName = one["positionName"]
item["positionName"] = positionName
except:
item["positionName"] = u""
salary = one["salary"]
workYear = one["workYear"]
item["city"] = city
item["companyFullName"] = companyname
#item["companySize"] = companysize
item["district"] = district
item["education"] = education
item["jobNature"] = jobNature
#item["positionLables"] = positionLables
item["salary"] = salary
item["workYear"] = workYear
yield item
item.py
import scrapy
class LagouItem(scrapy.Item):
positionName = scrapy.Field()
city = scrapy.Field()
companyFullName = scrapy.Field()
district = scrapy.Field()
education = scrapy.Field()
jobNature = scrapy.Field()
positionLables = scrapy.Field()
salary = scrapy.Field()
workYear = scrapy.Field()
pipelines.py
修改下秽五,請(qǐng)教了下程老哥,先試著理解下
import MySQLdb
def dbHandle():
conn = MySQLdb.connect(
host = "127.0.0.1",
user = "root",
passwd = "882645",
charset = "utf8",
db = "Lagou",
use_unicode = False
)
return conn
#連接數(shù)據(jù)庫(kù)饥悴,所需要配置一下數(shù)據(jù)庫(kù)的基本信息
class LagouPipeline(object):
def process_item(self, item, spider):
dbObject = dbHandle()
cursor = dbObject.cursor()
#調(diào)用上面所編寫(xiě)的函數(shù)坦喘,還有其中的一個(gè)方法cursor,用于提交
sql = "insert into lagou.jobs(positionName,city,companyFullName,district,education,jobNature,positionLables,salary,workYear ) values(%s,%s,%s,%s,%s,%s,%s,%s,%s)"
#編寫(xiě)插入數(shù)據(jù)庫(kù)的語(yǔ)句
try:
cursor.execute(sql,
(item['positionName'], item['city'], item['companyFullName'], item['district'], item['education'], item['jobNature'],
item['positionLables'], item['salary'], item['workYear']))
#連接item
cursor.connection.commit()
#提交item
except BaseException as e:
print u"錯(cuò)誤在這里>>>>", e, u"<<<<錯(cuò)誤在這里"
return item
作業(yè)結(jié)果展示
作業(yè)中遇到的問(wèn)題
這次所選用的是scrapy + mysql
問(wèn)題一:
為什么用scrapy呢西设?本來(lái)是打算寫(xiě)一個(gè)不是框架版的爬蟲(chóng)的瓣铣,但是問(wèn)題就卡在函數(shù)返回值到另一個(gè)函數(shù)的傳遞,不知道如何來(lái)傳遞贷揽,不知道返回的值應(yīng)該如何用棠笑,而且本來(lái)是想用類(lèi)的,也是因?yàn)椴皇煜で菪鳎詿o(wú)奈選擇放棄蓖救,說(shuō)明基礎(chǔ)不扎實(shí),還得好好補(bǔ)一下函數(shù)和類(lèi)這一塊印屁,所以就選擇了scrapy
問(wèn)題二:
在爬取數(shù)據(jù)保存的過(guò)程中
第一次是保存成csv文件的循捺,但是很奇怪,會(huì)多次出現(xiàn)大量空行雄人,不知道是什么原因造成的巨柒,第二次是保存到mysql文件的,保存了幾百個(gè)數(shù)據(jù)后就停止了柠衍,而且在程序運(yùn)行中反復(fù)出現(xiàn)以下提示:
一開(kāi)始以為是positionName的原因,然后在positionName那里加了try語(yǔ)句還是出現(xiàn)這樣的提示晶乔,不知道如何解決珍坊。
感謝
在做作業(yè)的過(guò)程中,非常感覺(jué)@liang和@程老哥 幫助正罢,這么晚了還這么有耐心的幫忙解決問(wèn)題阵漏,真得是非常感謝。