告别裸奔,赶集抓手

  • 2019 年 10 月 5 日
  • 筆記

告别裸奔,赶集抓手

1.告别裸奔

2.赶集抓手

3.作者的话

1.告别裸奔

裸奔

在爬虫过程中,有时有些网站具有反爬虫设置,当爬取次数到达一定程度,那么这个网站就会禁止你的IP对其进行访问,这就是裸奔操作,为了不让对方服务器发现你在爬取对面的网站信息。

换句话说,以隐藏身份爬取对应网站,那么这里就采取从西刺网站爬取国内高匿代理IP设置代理参数,从而隐藏自己,接下来先来看一下,如何实现西刺ip的爬取及处理呢?

西刺代理:

http://www.xicidaili.com/nn

分析

西刺分析图

在上图中,三个红色框,分别表示,ip,端口,以及类型,最终所要实现的结果是:{'HTTP':'HTTP://ip:port'}

这里我只是利用西刺的数据,去爬取赶集网数据。所以这里只选择了4页数据进行处理,如果想要更多数据,去建立一个自己的代理池,那么只需要变动循环次数,或者获取下一页的url即可进行多页面获取!

代码

baseurl = 'http://www.xicidaili.com/nn/'  headers = {      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',  }  http_list = []  def get_IP():      print('-----IP爬取进度-----')      # 只爬取4页的ip      for i in range(1,5):          print('------第' + str(i) + '页开始爬取------')          url = baseurl + str(i)          raw_html = requests.get(url, headers=headers).text          # print(raw_html)          selector = etree.HTML(raw_html)          # td[index]中index从1开始,分别获取ip,port,type          ip = selector.xpath('//tr[@class="odd"]//td[2]/text()')          port = selector.xpath('//tr[@class="odd"]//td[3]/text()')          httptype = selector.xpath('//tr[@class="odd"]//td[6]/text()')          # 上述ip/port/type均为list数据(存储的是当页数据),我们需要将其每一个数据转换为{'HTTP':'HTTP://ip:port'}格式,用在requests.get里面的proxies参数里面!          for eachip,eachport,eachtype in zip(ip,port,httptype):              http_dict = {}              http_dict[eachtype] = eachtype + '://' + eachip + ':' + eachport              http_list.append(http_dict)          print(http_list) # 两页总共的ip          print('------第' + str(i) + '页爬取结束------')        # 返回的数据格式为[{},{},{}.....]      return http_list  print('------IP爬虫结束------')

数据

IP结果图

2.赶集抓手

分析

本次主要想获取赶集网二手房信息,如下列字段:

数据字段图

在分析中发现两个问题:

第一:网站反爬虫处理,当访问次数过多,就会有验证操作,如下图即为当前验证操作的源码。

反爬虫源码图

第二:我们直观看到只有10个页面,但是当你点击第10个页面(如下图)的时候会发现,后面又有新的页面(如下图)了,于是这里就不能直接通过获取页面总个数,进行遍历,那么该如何操作呢?

对于多页面的处理除了上述,还有两种。1.模拟js或者触发相应事件;2.直接获取下一页的url,进行拼接即可。从上述方法中,我选择了第二种,那么这个多页面问题就又解决了。

以下分别为打开赶集首页以及点击第10页后的页面!

前10页图

10页后图

功能

  • 西刺IP本地存储及读取
  • 通过西刺IP爬页面
  • 数据提取
  • 美化打印
  • 数据库存储(包括mysql及mongodb)

这里先给大家看一下,最后的运行结果,有个直观的感受。

mongodb存储图

mysql存储图

当前为第5页数据,有368条,前4个页面,每页1000条,那么总共4368条,同上面的mongodb一致!

csv数据图

mysql导出数据表,共4369行,减掉第一行的字段,共4368行。

终端打印图

实现

  • 封装
MONGO_URI = 'localhost'  MONGO_DB = 'test' # 定义数据库  MONGO_COLLECTION = 'ganji' # 定义数据库表  class ganji_spider(object):      def __init__(self,mongo_uri,mongo_db):          self.client = pymongo.MongoClient(mongo_uri)          self.db = self.client[mongo_db]
  • 获取源码
def get_html(self, url, proxies):      headers = {          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',      }      raw_html = requests.get(url, headers=headers, proxies=proxies).text      return raw_html
  • 西刺IP存储
def save_proxies(self, http_list):      ouf = open("valid_ip.txt", "a+")      for each_proxies in http_list:          ouf.write(str(each_proxies))          ouf.write('n')
  • 随机行读取上述存储的IP(每一行代表一个proxies),利用代理爬取网页
def avoid_verifi(self, http_list, url):      # print(http_list)      # print(len(http_list))      while True:          random_line = random.randint(1, len(http_list))          theline = open('valid_ip.txt', 'r').readlines()[random_line]          theline = theline.replace("'", '"')  # 单引号全部替换为双引号          theline = json.loads(theline)  # str转dict(字典)格式          print("正在使用的代理IP:" + str(theline))          try:              html = self.get_html(url, theline)              # print(html)              if "访问过于频繁,本次访问做以下验证码校验" not in html:                  return html          except Exception as e:              print('.....')
  • 数据提取

下面异常处理的目的是防止缺失字段,对缺失字段处理!

def get_AllPage(self, url):      raw_html = self.avoid_verifi(http_list, url)      selector = etree.HTML(raw_html)      try:          # 房子名称          hose_title = selector.xpath('//dd[@class="dd-item title"]//a/text()')      except Exception as e:          hose_title = 0          print('房名error!')      try:          # 房子价格          hose_price = selector.xpath(              '//dd[@class="dd-item info"]//div[@class="price"]/span[@class="num js-price"]/text()')      except Exception as e:          hose_price = '0'          print('房价error!')      try:          # 房子单价          hose_eachprice = selector.xpath('//dd[@class="dd-item info"]//div[@class="time"]/text()')      except Exception as e:          hose_eachprice = '0'          print('单价error!')      try:          # 房子户型          house_type = selector.xpath('//dd[@class="dd-item size"]//span[@class="first js-huxing"]/text()')      except Exception as e:          house_type = '0'          print('户型error!')      try:          # 房子地址          span_list = selector.xpath('//dd[@class="dd-item address"]//span')          house_site = []          for each_span in span_list:              # string(.) 标签套标签              each_site = each_span.xpath('string(.)')              # 去掉换行及空格              each_site = each_site.replace('n', '').replace(' ', '')              house_site.append(each_site)      except Exception as e:          house_site = '0'          print('地址error!')      try:          # 房子大小          hose_size = selector.xpath('//dd[@class="dd-item size"]/span[3]/text()')      except Exception as e:          hose_size = '0'          print('大小error!')      return hose_title, hose_price, hose_eachprice, house_type, house_site, hose_size
  • 数据库存储

下面存储包含两大存储:mongodb与mysql

def Save_DB(self, hose_title, hose_price, hose_eachprice, house_type, house_site, hose_size):      for htitle, hprice, hpr, htype, hsite, hsize in zip(hose_title, hose_price, hose_eachprice, house_type, house_site,                                                          hose_size):          data = {}          data['房子名称'] = htitle          data['房子价格'] = hprice          data['房子单价'] = hpr          data['房子类型'] = htype          data['房子地址'] = hsite          data['房子大小'] = hsize          # mongodb存储          self.db[MONGO_COLLECTION].insert_one(data)          self.client.close()          # mysql存储          connection = pymysql.connect(host='localhost', user='root', password='xxxx', db='scrapydb',                                       charset='utf8mb4')          try:              with connection.cursor() as cursor:                  sql = "insert into `ganji`(`房子名称`,`房子价格`,`房子单价`,`房子类型`,`房子地址`,`房子大小`)values(%s,%s,%s,%s,%s,%s)"                  cursor.execute(sql, (htitle, hprice, hpr, htype, hsite, hsize))              connection.commit()          finally:              connection.close()

【调用】

if __name__ == '__main__':      # 类初始化      ganji = ganji_spider(MONGO_URI,MONGO_DB)      # 调用西刺IP      http_list = get_IP()      # 存储IP      ganji.save_proxies(http_list)      page_number = 1      index = 1      pagedict = {}      while True:          print("-----第" + str(page_number) + "页爬取开始------")          ganji_data = {}          baseurl = 'http://cq.ganji.com'          if page_number==1:              page_link = baseurl + '/fang5/o1/'              pagedict[str(page_number)] = page_link          # 代理获取真实的html          raw_html = ganji.avoid_verifi(http_list,pagedict[str(page_number)])          selector = etree.HTML(raw_html)            hose_title, hose_price, hose_eachprice, house_type, house_site, hose_size = ganji.get_AllPage(pagedict[str(page_number)])          # PrettyTable格式化打印          tb = pt()          tb.add_column('房子名称',hose_title)          tb.add_column('房子价格',hose_price)          tb.add_column('房子单价',hose_eachprice)          tb.add_column('房子类型',house_type)          tb.add_column('房子地址',house_site)          tb.add_column('房子大小',hose_size)          print(tb)          t1 = time.time()          ganji.Save_DB(hose_title, hose_price, hose_eachprice, house_type, house_site, hose_size)          t = time.time() - t1          # 计算数据存储耗时          print('存储耗时:' + str(t))          try:              page_nextText = selector.xpath('//ul[@class="pageLink clearfix"]//li//a[@class="next"]/span/text()')[0]              print(page_nextText)          except Exception as e:              # 避免到最后一页报错!              break              print('爬虫完毕!')          page_nextUrl = selector.xpath('//ul[@class="pageLink clearfix"]//li//a[@class="next"]/@href')[0]            print(page_nextUrl)          if page_nextText == '下一页 >':              index += 1              print(index)              pagedict[str(index)] = baseurl + page_nextUrl              print(pagedict)              print("-----第" + str(page_number) + "页爬取结束,5秒后重抓------")          else:              print("-----第" + str(page_number) + "是最后一页,没有下一页,爬虫结束---")              break            page_number += 1          print(page_number)          # time.sleep(5) 没有代理ip,需要加上此行!        print("------爬虫全部结束------")

3.作者的话

如果您觉得本公众号对您有帮助,请多多转发、交流及支持~~~谢谢!

项目地址,请点击阅读原文哦!觉得可以,希望给个star!