• 128719

    文章

  • 810

    评论

  • 12

    友链

  • 最近新加了换肤功能,大家多来逛逛吧~~~~
  • 喜欢这个网站的朋友可以加一下QQ群,我们一起交流技术。

爬取猫眼电影--静态网页反爬与多线程/多进程爬取

服了这份高薪指南,涨多少你说了算>>
点击上方“蓝字”关注我们




本篇爬虫我们将爬取猫眼电影的TOP100排行榜并优化程序,使用多线程/多进程进行数据爬取。
如果你是小白可以先查看较为基础的静态爬虫文章:Bilibili每日排行榜爬虫这篇文章中包含静态网页的爬取,利用正则进行的数据提取,数据的写入等,使用的方法也为比较基础



01

网页解析


首先到猫眼Top100榜单页面,可以看到总共有十页数据,每页数量为10部电影。点击第二页,可以看到URL为https://maoyan.com/board/4?offset=20,后续点击其他页数也有相同的规律,基本可以推断出offset参数为偏移量,只需要构造URL并改变偏移量即可。

确定URL后我们右键查看网页源代码,发现源代码中已包含所有我们需要的数据,基本可以确定这是一个静态网页,因此只需要请求网页内容并解析即可。

02


爬取代码


请求网址

利用request获取html信息,并进行异常处理

def get_html_text(url, header):    try:        response = requests.get(url, headers=header, timeout=30)        response.encoding = 'utf-8'        if response.status_code == 200:            print("请求成功")            return response.text    except requests.exceptions.RequestException:        print("请求失败,请检查网络条件或重新运行")


解析HTML

之后使用正则将上一步返回的html文本进行解析并返回提取的结果

def get_info_from_htmltext(html):#使用正则提取数据    pattern = re.compile('.*?board-index.*?>(\d+)</i>'                         + '.*?<p class="name"><.*?>(.*?)</a></p>'                         + '.*?<p class="star">(.*?)</p>'                         + '.*?<p class="releasetime">(.*?)</p>'                         + '.*?<p class="score"><i class="integer">(.*?)</i><i class="fraction">(.*?)</i></p>', re.S)    infomation = re.findall(pattern, html)    for info in infomation:        # 构造生成器函数,生成迭代对象        yield {            '排名': info[0],            '电影名称': info[1],            '主演': info[2].strip()[3:],#去除空格与换行符            '上映时间': info[3].strip()[5:],            '评分': info[4] + info[5]        }

这一段中我们使用 strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列,同时使用yield函数创建一个可迭代的字典对象。

关于yield函数的使用可以查看这一篇文章:http://smilecoc.vip/2020/10/10/python%E4%B8%ADyield%E7%9A%84%E7%94%A8%E6%B3%95%E8%AF%A6%E8%A7%A3/

数据保存

这一步骤中将上一步骤中的返回的数据保存到csv中

def save_data(data):    with open('miaoyan_top_100_film.csv', 'a', newline='', encoding='utf-8-sig', errors='ignore') as f:        csv_file = csv.writer(f)        csv_file.writerow([data['排名'], data['电影名称'], data['主演'], data['上映时间'], data['评分']])

主程序

最后完成主程序:

def main(page):   url="https://maoyan.com/board/4?offset=" + str(page * 10)   header = {       "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36",       "cookie": "__mta=217946409.1602755224603.1602820125740.1602820410838.8; uuid_n_v=v1; uuid=37FEFDA00ECB11EBB74D8BF542AFB5B820214EE9447685C70B6F27096D57; _csrf=a69cdd1c13dad15868d1d821fac56d68691a1af9466e3534930fadceea199; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1602755166; _lxsdk_cuid=17356de2c8-0c5a381c554641-4353760-100200-17356de2b62c8; _lxsdk=37FEFDA00ECB1EBB74D8BF51EFE42AFB5B820214EE9447685C70B6F27096D57; SL_GWPT_Show_Hide_tmp=1; SL_wptGlobTipTmp=1; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1602820411; _lxsdk_s=1752f852f16-684-f32-ead%7C%7C11"   }   html=get_html_text(url,header)   for one_page_data in get_info_from_htmltext(html):       print(one_page_data)       save_data(one_page_data)

if __name__ == '__main__': for i in range(10): main(i)

这里有一个注意的地方,如果在header只有User-Agent运行代码,返回了200,看似很成功,但即将引来第二个障碍——我们发现它虽然返回了html信息,但是仔细一看有一个大大的验证中心,并不是我们需要的html页面


针对这种情况,我们加上自己的cookies就可以破解反扒了。

同时为了增加爬取速度,我们可以使用多线程或者多进程爬取

03


多线程与多进程


关于多线程与多进程的概念请参阅:http://smilecoc.vip/2020/10/10/%E5%A4%9A%E8%BF%9B%E7%A8%8B%E5%92%8C%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%9A%84%E6%A6%82%E5%BF%B5/

多进程爬取

Python中使用多进程需要multiprocessing,以及提供了process、queue、pipe、lock、pool等组件,这里主要使用的是pool,可以叫做进程池。

使用pool前需要先了解:

  • pool = Pool(n): 建立进程池,n就是代表了建立几个进程,这个n的设定一般与cpu的核数一样

  • **pool.map(def,list):**把列表list里面的每一项映射到你所定义的def函数内,有点通过这句话做list各项循环的意味

  • **pool.close():**关闭进程池,不再接受新的进程

  • **pool.join():**主进程阻塞等待子进程安全退出,父子进程同步
    多进程爬取代码如下:


  • if __name__ == '__main__':    pool=Pool(4)#根据电脑情况设置    pool.map(main,(i for i in range(10)))

多线程爬取

同样多进程爬取需要用到threading模块。启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行,基本语法为:

  • **threading.Thread(target=程序名,args=(传入参数)): **创建线程

  • **Thread.run(): ** 用以表示线程活动的方法。

  • **Thread.start(): **启动线程活动。

  • **Thread.join([time]): ** 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。

本例中实现多线程需要对主函数进行修改,主要代码为:

#多线程    if __name__ == '__main__':    t1 = Thread(target=main, args=(0, 5))    t2 = Thread(target=main, args=(5, 10))    t1.start()    t2.start()

多线程和多进程编程,模型复杂,容易发生冲突。例如在本例中使用多线程,多进程写入数据的话会造成一些不可预知的错误,所以必须用锁加以隔离,同时,又要小心死锁的发生,或者需要将各个线程/进程的数据保存在内存中再写入才可以避免错误。

具体代码可以公众号后台回复:猫眼电影Top100爬虫获取

本文分享自微信公众号 - Romi的杂货铺(gh_9bf9a95e00cb)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。


695856371Web网页设计师②群 | 喜欢本站的朋友可以收藏本站,或者加入我们大家一起来交流技术!

0条评论

Loading...


发表评论

电子邮件地址不会被公开。 必填项已用*标注

自定义皮肤 主体内容背景
打开支付宝扫码付款购买视频教程
遇到问题联系客服QQ:419400980
注册梁钟霖个人博客