使用requests做爬虫

爬虫基础

  1. 爬虫:模拟浏览器上网,抓取数据。

  2. www.baidu.com/robots.txt

  3. 常用请求头

    • User-Agent
    • Connection:请求完毕后,是断开还是保持连接
    • cookie:
  4. 常用响应头

    • Content-Type:服务器响应回客户端的数据类型
  5. https

    • 安全的超文本传输协议
    • 加密方式:
      • 对称密钥加密
      • 非对称密钥加密
      • 证书密钥加密

requests

  1. 使用:

    • 确定url
    • 发送请求
    • 获取响应数据
    • 存储
  2. get方法

    requests.get(url= , params= ,headers= )

    params为url参数

    headers:UA伪装

  3. post方法

    requests.post(url= , data= headers= )

    data类似params

  4. json

    • json.dumps()函数是将字典转化为字符串

    • json.dump()函数的使用,将json信息写进文件

    • json.loads()函数是将字符串转化为字典

      with open('BVid.json', 'w', encoding='utf-8') as fp:
          json.dump(dict_BV,fp=fp,ensure_ascii=False)
          #因为json.dumps 序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定		#ensure_ascii=False
      
    • json.load()函数的使用,将读取json信息

      with open('1.json', 'r') as f:
        data = json.load(f)
      
    • 客户端要向服务器端发送一个json字符串,服务器端要接收并处理。下面演示正确的代码:

      import json
      with open('1.json', 'r') as f:
          data = json.load(f)
      data = {"company_data": json.dumps(data)}
       
      # urlopen()的data参数默认为None,当data参数不为空的时候,urlopen()提交方式为Post
      from urllib import request, parse
      url = r'http://192.168.165.4:8000/show/report/'
      company_data = parse.urlencode(data).encode('utf-8')
      req = request.Request(url, data=company_data)
      print(company_data)
      page = request.urlopen(req).read()
      page = page.decode('utf-8')
       
      print('page是什么', page, type(page))
      
    • 接收的话,下面的代码只是接收并打印,并没有进行一系列的判断然后去入库之类的,因为我还没做:

      def receive_data(request):
          if request.method == 'POST':
          print(request.get_full_path())
          print(request.body)
          data = request.POST.get("company_data")
          if data:
              try:
                  data = json.loads(data)
                  print("企业数据", type(data), data)
                  return HttpResponse(data)
              except ValueError as e:
                  print(str(e))
          else:
              return HttpResponse("no data")
      
  5. requests.session()

    requests库的session会话对象可以跨请求保持某些参数,说白了,就是比如你使用session成功的登录了某个网站,则在再次使用该session对象求求该网站的其他网页都会默认使用该session之前使用的cookie等参数

    • 例:

      #requests.session():维持会话,可以让我们在跨请求时保存某些参数
      import requests
      #实例化session
      session = requests.session()
      #目标url
      url = 'https://www.douban.com/accounts/login'
      form_data = {
          'source': 'index_nav',
          'form_email': 'xxx',
          'form_password': 'xxx',
          'captcha-solution': 'stamp',
          'captcha-id': 'b3dssX515MsmNaklBX8uh5Ab:en'
      }
      #设置请求头
      req_header = {
          'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
      }
      #使用session发起请求
      response = session.post(url,headers=req_header,data=form_data)
      if response.status_code == 200:
       
          #访问个人主页:
          url = 'https://www.douban.com/people/175417123/'
       
      	response = session.get(url,headers = req_header)
       
          if response.status_code == 200:
       
              with open('douban3.html','w') as file:
       
                  file.write(response.text)
      

  1. 手动设置cookie

    • cookie格式化(手动)

      # 处理cookie内容为字典
      cookie = "SINAGLOBAL=821034395211.0111.1522571861723; wb_cmtLike_1850586643=1; un=tyz950829@sina.com; wb_timefeed_1850586643=1; "
      cookie_dict = {i.split("=")[0]: i.split("=")[1] for i in cookie.split("; ")}
      cookie_dict = {i.split("=")[0]:i.split("=")[-1] for i in cookie.split("; ")}
      response =  seesion.get(url=url, headers=header, cookies=cookie_dict)
      
    • try:
          res = requests.post(url=login_url, headers=headers, data=body)
          cookies = res.cookies
          cookie = requests.utils.dict_from_cookiejar(cookies)
          return cookie
      except Exception as err:
          print('获取cookie失败:\n{0}'.format(err))
      
    • requests.utils.dict_from_cookiejar(cj)
      requests.utils.cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True)
      requests.utils.add_dict_to_cookiejar(cj, cookie_dict)
      
  2. 批量添加引号:

    (\S*)😦.\S)

    ‘$1’:‘$2’,

    (\S*): ([0-9a-zA-Z]*)

    ‘$1’:‘$2’,

    表示整个正则表达式匹配到的内容,表示捕获组匹配到的内容(其中,n>=1)

  3. response = requests(url)
    
    #改变编码
    response.encoding = 'utf-8'
    #通用处理中文乱码的解决方刻
    img_name = img_name.encode(iso-8859-1).decode('gbk')
    #返回字符串
    response.text
    #返回对象
    response.json()
    #二进制
    response.content
    

Python File(文件) 方法

  1. open()
    open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
    

    参数说明:

    • file: 必需,文件路径(相对或者绝对路径)。
    • mode: 可选,文件打开模式
    • buffering: 设置缓冲
    • encoding: 一般使用utf8
    • errors: 报错级别
    • newline: 区分换行符
    • closefd: 传入的file参数类型
    • opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。

    mode 参数有:

    t文本模式 (默认)。
    x写模式,新建一个文件,如果该文件已存在则会报错。
    b二进制模式。
    +打开一个文件进行更新(可读可写)。
    U通用换行模式(不推荐)。
    r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
    rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。
    r+打开一个文件用于读写。文件指针将会放在文件的开头。
    rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。
    w打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
    wb以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
    w+打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
    wb+以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
    a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
    ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
    a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
    ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
  2. file 对象
    序号方法及描述
    1file.close()关闭文件。关闭后文件不能再进行读写操作。
    2file.flush()刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。
    3file.fileno()返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。
    4file.isatty()如果文件连接到一个终端设备返回 True,否则返回 False。
    5file.next()返回文件下一行。
    6[file.read(size])从文件读取指定的字节数,如果未给定或为负则读取所有。
    7[file.readline(size])读取整行,包括 “\n” 字符。
    8[file.readlines(sizeint])读取所有行并返回列表,若给定sizeint>0,则是设置一次读多少字节,这是为了减轻读取压力。
    9[file.seek(offset, whence])设置文件当前位置
    10file.tell()返回文件当前位置。
    11[file.truncate(size])截取文件,截取的字节通过size指定,默认为当前文件位置。
    12file.write(str)将字符串写入文件,返回的是写入的字符长度。
    13file.writelines(sequence)向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。

Python 正则表达式

re 模块使 Python 语言拥有全部的正则表达式功能

  1. 正则表达式模式

    模式描述
    ^匹配字符串的开头
    $匹配字符串的末尾。
    .匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
    […]用来表示一组字符,单独列出:[amk] 匹配 ‘a’,‘m’或’k’
    [^…]不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
    re*匹配0个或多个的表达式。
    re+匹配1个或多个的表达式。
    re?匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
    re{ n}精确匹配 n 个前面表达式。例如, o{2} 不能匹配 “Bob” 中的 “o”,但是能匹配 “food” 中的两个 o。
    re{ n,}匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。“o{1,}” 等价于 “o+”。“o{0,}” 则等价于 “o*”。
    re{ n, m}匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
    a| b匹配a或b
    (re)对正则表达式分组并记住匹配的文本
    (?imx)正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
    (?-imx)正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
    (?: re)类似 (…), 但是不表示一个组
    (?imx: re)在括号中使用i, m, 或 x 可选标志
    (?-imx: re)在括号中不使用i, m, 或 x 可选标志
    (?#…)注释.
    (?= re)前向肯定界定符。如果所含正则表达式,以 … 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。
    (?! re)前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功
    (?> re)匹配的独立模式,省去回溯。
    \w匹配字母数字及下划线
    \W匹配非字母数字及下划线
    \s匹配任意空白字符,等价于 [ \t\n\r\f]
    \S匹配任意非空字符
    \d匹配任意数字,等价于 [0-9].
    \D匹配任意非数字
    \A匹配字符串开始
    \Z匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。
    \z匹配字符串结束
    \G匹配最后匹配完成的位置。
    \b匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。
    \B匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
    \n, \t, 等.匹配一个换行符。匹配一个制表符。等
    \1…\9匹配第n个分组的内容。
    \10匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。
    [0-9a-fA-F]可以匹配数字,大小写形式的a~f
  2. findall
    在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。

    注意: match 和 search 是匹配一次 findall 匹配所有。

    语法格式为:

    findall(pattern, string, flags=0)
    

    参数:

    • string : 待匹配的字符串。
    • pattern:为正则表达式
    • flag为替换规则,如是否区分大小写。
    • 若匹配到一个及以上的结果返回一个list。
  3. re.subn与re.sub

    函数原型:subn(pattern, repl, string, count=0, flags=0)

     			 sub(pattern, repl, string, count=0, flags=0)
    

    参数:

    • pattern为正则表达式,
    • repl为替换的文本,
    • string是被匹配的文本,
    • count是替换次数,缺省为0,表示全部替换,
    • flag是匹配规则,如是否区别大小写等,可省略。

    subn返回的结果是一个元组(替换后的字符串,替换次数)。

    sub仅返回替换后的字符串。

  4. re.match函数
    re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

    函数语法

    re.match(pattern, string, flags=0)
    

    函数参数说明:

    参数描述
    pattern匹配的正则表达式
    string要匹配的字符串。
    flags标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志

数据解析

正则、bs4、xpath

  • 数据解析原理:
    • 定位标签
    • 提前标签、标签属性中存储的数据值
  1. bs4数据解析的原理:
    • 实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中
    • 通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取
    • 提供的用于数据解析的方法和属性:
      • soup.tagName:返回的是文档中第一次出现的tagName对应的标签
      • soup.find():
      • find(‘tagName’):等同于soup.div
      • 属性定位:
        soup.find(‘div’,class_/id/attr=‘song’)
        soup.find_all(‘tagName’):返回符合要求的所有标签(列表)
      • select:
      • select(某种选择器(id,class,标签…选择器)),返回的是一个列表。
        层级选择器:
        soup.select(‘.tang>ul>1i>al’):>表示的是一个层级
        soup.select(‘.tang>ul a’):空格表示的多个层级
      • 获取标签之间的文本数据:
        soup.a.text/string/get_text()
        text/get_text():可以获取某一个标签中所有的文本内容
        string:只可以获取该标签下面直系的文本内容
        获取标签中属性值:
      • soup.a[‘href’]
  2. xpath:
    • 实例化一个etree对象:

      • etree.parse(fileName)
      • etree.HTML(page_text)
      • etree.tostring()方法即可输出修正后的HTML代码,但是结果是bytes类型
    • 定位

      • 标签/ 或 // 或 ./ 或 …/
      • 属性 [@attribute]
      • 取文本 /text()
      • alltext = data.xpath(‘string(.)’)
      • 取属性 /@attr
  • 谓语:

    • [1]
    • [last()]
    • [last()-1]
    • [position()❤️] 前面的两个
    • [@lang=‘eng’]
    • [price>35.00]
  • 选取未知节点

    • *匹配任何元素节点。
      /*/*/*/BBB 选择所有的有3个祖先元素的BBB元素
    • @*匹配任何属性节点
    • node()匹配任何类型的节点
  • XPath 运算符

    • 运算符描述实例返回值
      |计算两个节点集//book | //cd返回所有拥有 book 和 cd 元素的节点集
      +加法6 + 410
      -减法6 - 42
      *乘法6 * 424
      div除法8 div 42
      =等于price=9.80如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 false。
      !=不等于price!=9.80如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。
      <小于price<9.80如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
      <=小于或等于price<=9.80如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
      >大于price>9.80如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。
      >=大于或等于price>=9.80如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 false。
      orprice=9.80 or price=9.70如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。
      andprice>9.00 and price<9.90如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。
      mod计算除法的余数5 mod 21

验证码登录

  1. 获取验证码图片
  2. 识别验证码
  3. 获取登录url(post方法、勾选preserve log)
  4. 登录,查看response.status_code

IP代理

  1. import requests
    proxies = {
      "http": "10.10.1.10:3128", #http只能访问http类型
      "https": "10.10.1.10:1080",#https只能访问https类型
    }
    
    requests.get("http://example.org", proxies=proxies)
    
  2. 若你的代理需要使用HTTP Basic Auth,可以使用 http://user:password@host/ 语法:

    #私密代理指定的是付费的代理,需要用户名和密码
    proxies = {
        "http": "user:pass@10.10.1.10:3128/",
    }
    
  3. 要为某个特定的连接方式或者主机设置代理,使用 scheme://hostname 作为 key, 它会针对指定的主机和连接方式进行匹配。

    proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}
    

异步爬虫

  1. 多线程(threading 模块):

    • https://blog.csdn.net/wqx521/article/details/82754617
      https://www.runoob.com/python3/python3-multithreading.html
      
    • 模板:

      import threading
      import time
      
      class myThread (threading.Thread):
          def __init__(self, threadID, name, counter):
              threading.Thread.__init__(self)
              self.threadID = threadID
              self.name = name
              self.counter = counter
          def run(self):
              print ("开启线程: " + self.name)
              # 获取锁,用于线程同步
              threadLock.acquire()
              print_time(self.name, self.counter, 3)
              # 释放锁,开启下一个线程
              threadLock.release()
      
      def print_time(threadName, delay, counter):
          while counter:
              time.sleep(delay)
              print ("%s: %s" % (threadName, time.ctime(time.time())))
              counter -= 1
      
      threadLock = threading.Lock()
      threads = []
      
      # 创建新线程
      thread1 = myThread(1, "Thread-1", 1)
      thread2 = myThread(2, "Thread-2", 2)
      
      # 开启新线程
      thread1.start()
      thread2.start()
      
      # 添加线程到线程列表
      threads.append(thread1)
      threads.append(thread2)
      
      # 等待所有线程完成
      for t in threads:
          t.join()
          #Join的作用是阻塞进程直到线程执行完毕。通用的做法是我们启动一批线程,最后join这些线程结束
          #此处join的原理就是依次检验线程池中的线程是否结束,没有结束就阻塞直到线程结束,如果结束则跳转执行下一个线程的join函数
          #通过传给join一个参数来设置超时,也就是超过指定时间join就不在阻塞进程。
      print ("退出主线程")
      
  2. 多进程(multiprocessing模块):

    • multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,>提供了Process、Queue、Pipe、Lock等组件

    • https://blog.csdn.net/ctwy291314/article/details/89358144
      
    • 模板:

      import multiprocessing as mul
      
      
      def f(x):
          return x ** 2
      
      
      if __name__ == '__main__':
          pool = mul.Pool(5)
          rel = pool.map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
          pool.close()#进程池不再创建新的进程
          pool.join() #wait进程池中的全部进程。必须对Pool先调用close()方法才能join
          print(rel)
      
      

random

import random

print( random.randint(1,10) )        # 产生 1 到 10 的一个整数型随机数  
print( random.random() )             # 产生 0 到 1 之间的随机浮点数
print( random.uniform(1.1,5.4) )     # 产生  1.1 到 5.4 之间的随机浮点数,区间可以不是整数
print( random.choice('tomorrow') )   # 从序列中随机选取一个元素
print( random.randrange(1,100,2) )   # 生成从1到100的间隔为2的随机整数

print random.choice('abcdefghijklmnopqrstuvwxyz!@#$%^&*()')# 随机字符:
print random.sample('zyxwvutsrqponmlkjihgfedcba',5)     # 多个字符中生成指定数量的随机字符:

# 从a-zA-Z0-9生成指定数量的随机字符:
ran_str = ''.join(random.sample(string.ascii_letters + string.digits, 8))
print ran_str

# 多个字符中选取指定数量的字符组成新字符串:
print(''.join(random.sample(['z','y','x','w','v','u','t','s','r','q','p','o','n','m','l','k','j','i','h','g','f','e','d','c','b','a'], 5))) 

# 随机选取字符串:
print random.choice(['剪刀', '石头', '布'])

# 打乱排序
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
print random.shuffle(items)

a=[1,3,5,6,7]                # 将序列a中的元素顺序打乱
random.shuffle(a)
print(a)

笔记

  • 动态加载,ajax请求,局部刷新,XHR

  • __name__:

    1. 如果模块是被导入,__name__的值为模块名字
    2. 如果模块是被直接执行,__name__的值为’__main__’
    
  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值