感觉距离上次交作业已经很久了...
已经落后大家这么多了
思路分析
上次交的作业虽然能够爬取到数据,但是和大家的不同,完全偏离主题呀,心好痛
这次作业的关键点是构造表单,附带cookies发送请求
具体的思路前面的同学已经分析的很详细了,就不作一一的论述了
所以这次作业的主题是总结与探讨两个问题
为什么带cookies可以反ban?
想要解决这个问题,就要弄清楚几个概念
cookies(来自维基百科)
因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。在典型的网上购物场景中,用户浏览了几个页面,买了一盒饼干和两饮料。最后结帐时,由于HTTP的无状态性,不通过额外的手段,服务器并不知道用户到底买了什么。 所以Cookie就是用来绕开HTTP的无状态性的“额外手段”之一。服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态。
从cookies的定义可以看出,cookies也是可以作为一个验证用户身份的工具,所以可以通过cookies来区别机器和人,所以有一种反爬的策略,就是通过cookies,拉勾网的反爬虫机制就是基于cookies,所以同一个cookies可以重复请求,而同一个IP不带cookies却是不能重复请求,会封IP,但是拉勾网做的还是不够,就是只需要一个cookies,如果再严格一点,使cookies所保存的时间短一点,也许就需要一个cookies池了,定期加入cookies,这样爬取的难度就会增大很多,再厉害一点,就是IP和cookes一起识别。
总结一下目前所遇到的爬取方式
目前所爬取的网站只有三个,还是比较少的
但是我觉得都挺有收获和代表性的
以源码显示网站
这个基本的是有多基本呢?只需要正常的提交请求,就能够得到网站的源码,然后选取自己想要的信息,比如说有些教务网和一些小型的网站
以json等格式显示的网站
如简书的专题数据,这类的数据,我们所获取的内容不是网站的源码,所获取的是其返回的json等格式的包,所以获取信息的关键在于解析这个包来得到自己想要的内容
Ajax形式网站
首先来个科普
维基百科
传统的Web应用允许用户端填写表单(form),当提交表单时就向网页服务器发送一个请求。服务器接收并处理传来的表单,然后送回一个新的网页,但这个做法浪费了许多带宽,因为在前后两个页面中的大部分HTML码往往是相同的。由于每次应用的沟通都需要向服务器发送请求,应用的回应时间依赖于服务器的回应时间。这导致了用户界面的回应比本机应用慢得多。
与此不同,AJAX应用可以仅向服务器发送并取回必须的数据,并在客户端采用JavaScript处理来自服务器的回应。因为在服务器和浏览器之间交换的数据大量减少(大约只有原来的5%)
这一类的网站有如拉勾网,这一类网站的特点和上一种有点类似,但是为什么要分开来说呢?因为上一种情形一般会和源码一起结合出现,而Ajax网站,需要用户构造表单,向服务器要求返回想要的内容,而且这一类网站,一般是几乎所有信息(除了导航栏之类的通用信息)外,都是通过json, xml返回的,所以只需要从这个json包中便能获取所有数据。
作业代码
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数据分析']
citys = ['北京', '上海', '深圳', '广州', '杭州', '成都', '南京', '武汉', '西安', '厦门', '长沙', '苏州', '天津']
#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
修改下,请教了下程老哥,先试着理解下
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
#连接数据库,所需要配置一下数据库的基本信息
class LagouPipeline(object):
def process_item(self, item, spider):
dbObject = dbHandle()
cursor = dbObject.cursor()
#调用上面所编写的函数,还有其中的一个方法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)"
#编写插入数据库的语句
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"错误在这里>>>>", e, u"<<<<错误在这里"
return item
作业结果展示
作业中遇到的问题
这次所选用的是scrapy + mysql
问题一:
为什么用scrapy呢?本来是打算写一个不是框架版的爬虫的,但是问题就卡在函数返回值到另一个函数的传递,不知道如何来传递,不知道返回的值应该如何用,而且本来是想用类的,也是因为不熟悉,所以无奈选择放弃,说明基础不扎实,还得好好补一下函数和类这一块,所以就选择了scrapy
问题二:
在爬取数据保存的过程中
第一次是保存成csv文件的,但是很奇怪,会多次出现大量空行,不知道是什么原因造成的,第二次是保存到mysql文件的,保存了几百个数据后就停止了,而且在程序运行中反复出现以下提示:
一开始以为是positionName的原因,然后在positionName那里加了try语句还是出现这样的提示,不知道如何解决。
感谢
在做作业的过程中,非常感觉@liang和@程老哥 帮助,这么晚了还这么有耐心的帮忙解决问题,真得是非常感谢。