2017/4/27 cookies

感觉距离上次交作业已经很久了...
已经落后大家这么多了

思路分析

上次交的作业虽然能够爬取到数据,但是和大家的不同,完全偏离主题呀,心好痛
这次作业的关键点是构造表单,附带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和@程老哥 帮助,这么晚了还这么有耐心的帮忙解决问题,真得是非常感谢。

推荐阅读更多精彩内容