diff --git a/README.md b/README.md index c0cfa22..8444bf1 100644 --- a/README.md +++ b/README.md @@ -1 +1,221 @@ -IyDkuqzkuJzmmL7ljaHllYblk4Hkv6Hmga/liIbluIPlvI/niKzomasNCg0KIyMjIOS7i+e7jQ0KDQrmnKzniKzomavliIbkuKTkuKrpg6jliIY6DQoNCi0gIOesrOS4gOmDqOWIhuaQnOe0oumhteeIrOiZq+aYr+S9v+eUqHNlbGVuaXVt54is5Y+W5pCc57Si6aG1LCDkuLvopoHojrflj5bnmoTmmK/llYblk4HnmoRza3Xlkozku7fmoLwNCg0KLSDnrKzkuozpg6jliIbllYblk4HpobXniKzomavkvb/nlKhzY3JhcHnniKzlj5bor6bmg4XpobXnmoTllYblk4Hku4vnu43kv6Hmga8sIOS4u+imgeiOt+WPluWVhuWTgeeahOWei+WPtw0KDQrnlLHkuo7kuqzkuJzllYblk4Hnp43nsbvnuYHlpJosIOS4jeWQjOeahOWVhuWTgeWTgeexu+mXtOW3ruW8guW3qOWkpywg5ZWG5ZOB6aG154is6Jmr55qE5Luj56CB5Lmf5bCx5LiN6L+R55u45ZCMLiDlh7rkuo7miJDmnKzlkozml7bpl7TogIPomZEsIOaQnOe0oumhteeIrOiZq+S9v+eUqCoqMzA4MCoq5L2c5Li65pCc57Si5YWz6ZSu6K+NLCDllYblk4HpobXniKzomavniKzlj5bmkJzntKLpobXniKzomavojrflj5bnmoTllYblk4Hlr7nlupTnmoTor6bmg4XpobUNCg0K5YW25pW05L2T5p625p6E5aaC5LiLOg0KDQohW10oRDpccHl0aG9uX2NsYXNzXGpkLWRpc3RyaWJ1dGVkLWNyYXdsZXJcaW1nXDIwMjItMDQtMTUtMTYtMjgtMTAtaW1hZ2UucG5nKQ0KDQojIyMg55S15ZWG572R56uZ562b6YCJDQoNCueIrOiZq+eahOebrueahOWwseaYr+S4uuS6huiOt+WPluS4gOWumumHj+eahOacieS7t+WAvOeahOS/oeaBrywgIOWwseebruWJjeS4reaWh+S6kuiBlOe9keadpeivtCwg55S15ZWG572R56uZ5piv5L+h5oGv5a+G5bqm5ZKM5Lu35YC85a+G5bqm5q+U6L6D6auY55qE5Zyw5pa5LCDlm6DmraTkvb/nlKjniKzomavojrflj5bkv6Hmga/mnInkuIDlrprnmoTlj6/nlKjmgKcuDQoNCuebruWJjeiAjOiogCwg5reY5a6d6I635Y+W55qE5ZWG5ZOB5L+h5oGv57uT5p6c5bGC5qyh5pyA5aSaLCDlk4HnsbvmnIDlhaguIOS9huaYr+mmluWFiOa3mOWuneWVhuWTgeaQnOe0oumhtea3t+adguWkqeeMqyzlpKnnjKvlm73pmYUsIOa3mOWuneWNluWutuetieWQhOexu+WQhOS4jeebuOWQjOeahOW6l+mTuiwg5Zug5q2k5YW25ZWG5ZOB6K+m5oOF6aG157uT5p6E5Lmf5aSn55u45b6E5bqtOyDlhbbmrKHmmK/mt5jlrp3ljoblj7LljIXoorHmr5TovoPph40sIOWboOatpOacieiuuOWkmue7k+aehOa3t+S5seeahOmhtemdoua3t+adguWFtuS4rSwg6Zq+5Lul5o+Q5Y+W5pWw5o2uLg0KDQrlm6DmraQsIOWunumZheiAg+iZkeWQjuehruiupCwg5reY5a6d5LiN5piv5aSq6YCC5ZCI5paw5Lq65bCP5Zui6Zif5Y675a2m5Lmg6K+V54K854is6Jmr54is5Y+W5L+h5oGvLg0KDQrnm7jlr7nogIzoqIAsIOS6rOS4nOaQnOe0oumhteWfuuacrOWQjOS4gOS4uuS6rOS4nOiHquW3seeahOmhtemdoiwg5Lqs5Lic6Ieq6JClLCDml5foiLDlupflkozllYblrrblupfnu5PmnoTln7rmnKzmsqHmnInljLrliKssIOivpuaDhemhtee7k+aehOS5n+S4gOiHtCwg5a+554is6Jmr5q+U6L6D5Y+L5aW9KOS6rOS4nOWUr+S4gOS4jeWkquWPi+WlveeahOWcsOaWueWPquaciSwg5YW25Li756uZ6aG16Z2i5LiL5bm25rKh5pyJ5YaZcm9ib3RzLnR4dCkuIOWboOatpOiAg+iZkeeIrOWPluS6rOS4nC4NCg0KIyMjIOaQnOe0oumhteeIrOiZqw0KDQrkuLvopoHnlLFgc2V0dGluZ3MueWFtbGDlkoxgamRTZWFyY2hTZWxlbml1bVNwaWRlci5weWDkuKTpg6jliIbnu4TmiJAsYHNldHRpbmdzLnlhbWxg5piv6YWN572u5paH5Lu2LCAgYGpkU2VhcmNoU2VsZW5pdW1TcGlkZXIucHlg5piv5Luj56CB5omn6KGM5Li75L2TDQoNCiMjIyMg5YmN5pyf5bel5L2cDQoNCuWfuuacrOiAjOiogCwg5Lu75L2V5pyJ5pCc57Si5Yqf6IO955qE572R56uZ6YO95Lya5a+55YW25pCc57Si5Yqf6IO95YGa5LiA5a6a55qE6ZmQ5Yi2LCDku6XpmLLmraLljZXkuKppcOi/h+mrmOmikeeOh+eahOiuv+mXriwg5Y2g55So6L+H5aSa6LWE5rqQLg0KDQrkuqzkuJzkuZ/kuI3kvovlpJYsIOWvueS6juacqueZu+W9leeahOivt+axgiwg55Sa6Iez5piv5bim552AaGVhZGVy5ZKMY29va2ll55qE6K+35rGCLCDlvoDlvoDpg73mmK/np4nmjIHmi5Lnu53nmoTmgIHluqYsIOimgeaxgumqjOivgeeZu+W9lToNCg0KIVtdKEQ6XHB5dGhvbl9jbGFzc1xqZC1kaXN0cmlidXRlZC1jcmF3bGVyXGltZ1wyMDIyLTA0LTE1LTEzLTI1LTQ4LWltYWdlLnBuZykNCg0KIVtdKEQ6XHB5dGhvbl9jbGFzc1xqZC1kaXN0cmlidXRlZC1jcmF3bGVyXGltZ1wyMDIyLTA0LTE1LTEzLTI2LTQyLWltYWdlLnBuZykNCg0K5pCc57Si5byV5pOO55qE54is6Jmr6aqM6K+B5LiA6Iis57qn5Yir6YO95q+U6L6D6auYLCDpmr7ku6Xnu5Xov4cuIOiAg+iZkeWIsOaQnOe0oumhteeahOaVsOmHj+ebuOWvueS6juWVhuWTgemhteeahOaVsOmHj+W+gOW+gOaYr+WwkeS4gOS4quaVsOmHj+e6pywg5LiU5Lqs5Lic5Y2V5Liq5pCc57Si6K+N55qE6aG16Z2i6YeP5Lmf5aSn6Ie05Zyo5Yeg5Y2B5Yeg55m+6aG155qE5pWw6YeP57qn5LmL6Ze0LCDlm6DmraTkvb/nlKhweXRob27nmoRgc2VsZW5pdW1g5bqTLCDpqbHliqjmtY/op4jlmajljrvoh6rliqjljJborr/pl67mkJzntKLpobUsIOi/m+iAjOiOt+WPluaVsOaNriwg5piv5q+U6L6D566A5Y2V5LiU5Y+v6KGM55qE5pa55qGILg0KDQojIyMjIHNldHRpbmdzLnlhbWwNCg0KYHNldHRpbmcueWFtbGDmmK/mkJzntKLpobXniKzomavnmoTphY3nva7mlofku7YsIOWFtuWGheWuueWmguS4izoNCg0KYGBgeWFtbA0Kc2VhcmNoOg0KICBrZXl3b3JkOiAnMzA4MCcNCiAgc2xlZXA6IDUNCg0KcmVkaXM6IA0KICBob3N0OiAnMTIwLjI0Ljg3LjQwJw0KICBwb3J0OiAnNjM3OScNCiAgcGFzc3dvcmQ6ICdHdWV0QDIwNycNCiAgZGI6IDENCg0KbW9uZ29kYjoNCiAgaG9zdDogJzEyMC4yNC44Ny40MCcNCiAgcG9ydDogJzI3MDE3Jw0KICB1c2VybmFtZTogImFkbWluIg0KICBwYXNzd29yZDogIjEyMzQ1NiINCg0KcmVzdWx0Og0KICBycHVzaF9rZXk6ICJqZDpzdGFydF91cmxzIg0KYGBgDQoNCi0gYHNlYXJjaGANCiAgDQogIC0gYGtleXdvcmRgOiDopoHmkJzntKLnmoTlhbPplK7lrZcsIOWNs+WcqOS6rOS4nOWVhuWTgeaQnOe0ouS4iueahOimgeaQnOe0oueahOWVhuWTgTsNCiAgDQogIC0gYHNsZWVwYDog5LyR55yg5pe26Ze0LCDljZXkvY06IOenkjsg55Sx5LqOKipzZWxlbml1bSoq5pON5L2c6aG16Z2i55qE5Yqo5L2cKOS+i+WmgueCueWHu+e/u+mhtSwg5LiL5ouJKeaYryoq5byC5q2lKirnmoQsIOiAjOivu+WPlumhtemdouaYryoq5ZCM5q2lKirnmoQuIOS6rOS4nOmcgOimgeaJp+ihjOS4i+aLiei/meS4quaTjeS9nOWQjuaJjeWKoOi9veWHuuaJgOacieeahOWVhuWTgeS/oeaBrywg5Li65LqG6K+75Y+W5YWo5b2T5YmN55qE6aG16Z2i5L+h5oGvLCDpnIDopoHkvJHnnKDkuIDmrrXml7bpl7TnrYnlvoXlvILmraXmk43kvZzlrozmiJA7DQoNCi0gYHJlZGlzYA0KICANCiAgLSBgaG9zdGA6IHJlZGlz6YOo572y55qE5pyN5Yqh5Zmo5Zyw5Z2AOw0KICANCiAgLSBgcG9ydGA6IHJlZGlz6YOo572y5pe25byA5pS+55qE56uv5Y+j5Y+3Ow0KICANCiAgLSBgcGFzc3dvcmRgOiByZWRpc+mDqOe9suaXtuiuvue9rueahGF1dGg7DQogIA0KICAtIGBkYmA6IOmcgOimgeS9v+eUqHJlZGlz55qE56ys5Yeg5Liq5pWw5o2u5bqTDQoNCi0gYG1vbmdvZGJgDQogIA0KICAtIGBob3N0YDogTW9uZ29EQumDqOe9sueahOacjeWKoeWZqOWcsOWdgDsNCiAgDQogIC0gYHBvcnRgOiBNb25nb0RC6YOo572y5pe25byA5pS+55qE56uv5Y+j5Y+3Ow0KICANCiAgLSBgdXNlcm5hbWVgOiBNb25nb0RC6YOo572y5pe26K6+572u55qEYXV0aOeahOeUqOaIt+WQjSwg5oiW6ICF6K+06KaB5L2/55So55qETW9uZ29EQueahOaVsOaNruW6k+WQjTsNCiAgDQogIC0gYHBhc3N3b3JkYDogTW9uZ29EQumDqOe9suaXtuiuvue9rueahGF1dGguDQoNCi0gYHJlc3VsdGANCiAgDQogIC0gYHJwdXNoX2tleWA6IOeIrOiZq+iOt+WPluWIsOeahOaVsOaNruW9ouaIkOeahHVybOimgeaUvuWFpXJlZGlz5pe26K6+572u55qEa2V5LCB1cmzlnKhSZWRpc+S4reWtmOWCqOaWueW8j+aYr+WIl+ihqA0KDQojIyMjIGpkU2VhcmNoU2VsZW5pdW1TcGlkZXIucHkNCg0K5pCc57Si6aG154is6Jmr55qE56iL5bqP5YWl5Y+jLCDmoLnmja7kuqzkuJzmkJzntKLpobXpnaLniKzlj5bpobXpnaLlhYPntKAuDQoNCuWQr+WKqOeoi+W6j+WQjui/m+WFpeS6rOS4nOeahOWVhuWTgeaQnOe0oumhtemdoi4g5pCc57Si6aG155qE5pCc57Si5YWz6ZSu5a2X5Li6YHNldHRpbmdzLnB5YOS4reiuvuWumueahGBzZWFyY2gua2V5d29yZGAuDQoNCuS6rOS4nOeahOaQnOe0oumhtemdouS8muWIhuS4pOS4qumYtuauteWKoOi9vSwg56ys5LiA5Liq5Yqg6L296Zi25q615Yqg6L295oiQ5Yqf5ZCOKOWNs+WKoOi9veWHujMw5Liq5ZWG5ZOB55qE5L+h5oGvKSwg54is6Jmr5Lya5omn6KGMYHNjcm9sbF9ieV94cGF0aCgpYCwg5L2c55So5piv5rua5Yqo572R6aG15Yiw572R6aG15bqV6YOoLCDop6blj5HkuqzkuJzmkJzntKLpobXpnaLnmoTnrKzkuozkuKrliqDovb3pmLbmrrUsIOaKiuS4gOS4qumhtemdouS4iueahOS/oeaBr+mDveWKoOi9veWIsOa1j+iniOWZqOmHjC4NCg0KYHNjcm9sbF9ieV94cGF0aCgpYCAg5YaF6YOo5Y6f55CG5bCx5piv6amx5Yqo5rWP6KeI5Zmo56e75Yqo6KeG5Zu+LCDkvb/op4blm77lj6/ku6XnnIvliLDmjIflrprnmoTlhYPntKAuIOWcqOivpeeIrOiZq+S4rSwg5oyH5a6a55qE572R6aG15YWD57Sg55qEKip4cGF0aCoq5Li6YC8vKltAaWQ9IkpfYm90dG9tUGFnZSJdL3NwYW5bMV0vYVs5XWAsIOS7o+ihqOeahOaYr+S6rOS4nOaQnOe0oumhtemdoueahOS4i+S4gOmhteaMiemSrui/meS4gOe9kemhteWFg+e0oC4NCg0K5Lik5Liq6Zi25q616YO95Yqg6L295a6M5oiQ5ZCOLCDojrflj5bnvZHpobXkuK3miYDmnInllYblk4HnmoRza3Xlkozku7fmoLwuDQoNCiFbXShEOlxweXRob25fY2xhc3NcamQtZGlzdHJpYnV0ZWQtY3Jhd2xlclxpbWdcMjAyMi0wNC0xNS0xOC00MC0yMy1pbWFnZS5wbmcpDQoNCnNrdeaYr+S6rOS4nOe7meWVhuWTgeeahOe8luWPtywg5LiU5Y+v5Lul5qC55o2uc2t15ou85o6l5a+55bqU5ZWG5ZOB55qE6K+m5oOF6aG155qEVVJMLg0KDQrojrflj5bliLBza3Xlkozku7fmoLzlkI4sIOWwhnNrdeaLvOaOpeS4ulVSTCwgcnB1c2jliLBSZWRpc+iuvue9ruWlveeahOaVsOaNruW6k+eahGByZXN1bHQucnB1c2hfa2V5YOS4rSwg5bm25LiU5bCG5q+P5Liq5ZWG5ZOB55qE5pWw5o2u5LulYHsnc2t1Jzogc2t1LCAncHJpY2UnOiBwcmljZX1g55qE5b2i5byPLCDmlL7lhaVNb25nb0RC6K6+572u5aW955qE5pWw5o2u5bqT5LitLiDmlL7lhaVNb25nb0RC5YmN5Lya5qC55o2uc2t15a2X5q615p+l6K+i5pWw5o2u5piv5ZCm5a2Y5ZyoKOWOn+WtkOaTjeS9nCwg5Y+v5YiG5biD5byPKSwg6Ziy5q2i6YeN5aSN5o+S5YWlLg0KDQojIyMg5ZWG5ZOB6aG154is6JmrDQoNCuWVhuWTgemhteeIrOiZq+S9v+eUqOS6hmBzY3JhcHlfcmVkaXNg5Y675YaZLCDljbNgU2NyYXB5YOeahHJlZGlz54mI5pysLCDku5bkuI5gU2NyYXB5YOeahOS4u+imgeWMuuWIq+WwseaYr+aKiuWtmOaUvlVSTOS6jlJlaWRz5pWw5o2u5bqT5LitLCDliKnnlKhSZWRpc+eahOeJueaApywg5Y2z5Zyo5aSa5Liq6K+35rGC6K+35rGC5ZCM5LiA5Liq5pWw5o2u5pe2LCDkvJrpu5jorqTlr7nor7fmsYLmjpLluo8sIOacieS4gOS4qumYn+WIl+aViOaenCwg5L+d6K+B5LqG5YiG5biD5byP54is6Jmr6I635Y+W5Yiw5LiN5ZCM55qEVVJMLg0KDQpgc2NyYXB5X3JlZGlzYOahhuaetuS4i+eahOeIrOiZq+aJp+ihjOaYr+W8guatpeeahCwg5Y2zLCDku45SZWRpc+aVsOaNruW6k+eahOWIl+ihqOS4reiOt+WPllVSTOWQjiwg5Y+R6LW3UmVxdWVzdCwg5L6/5Y+v5Lul5omn6KGM5LiL5LiA5qyh5LuOUmVkaXPkuK3ojrflj5ZSZWRpcywg6ICM5Y+R6LW3UmVxdWVzdOW+l+WIsOeahHJlc3BvbnNl5YiZ562J5b6X5Yiw5ZCO5YaN55Sx5Zue6LCD5Ye95pWw5aSE55CGKOWNs+aJp+ihjGBqZHNrdS5weWDph4znmoRgcGFyc2Vg5pa55rOVKS4NCg0K6K+l54is6Jmr5Y+q6ZyA6KaB5ZyoKirmnKzniKzomavnm67lvZXkuIsqKiwg6LCD55SoYHNjcmFweSBjcmF3bCBqZHNrdWDljbPlj6/lkK/liqguDQoNCiMjIyMgc2V0dGluZ3MucHkNCg0K6YWN572u5paH5Lu2LCDlhbfkvZPphY3nva7mn6XnnItgU2NyYXB5YOeahOmFjee9riwg5YW25Lit5pyJ5Yeg5Liq5pivYHNjcmFweV9yZWRpc2DnmoTphY3nva7miJbmnKzmrKHpobnnm67lvJXlhaXnmoTphY3nva4sIOWcqOatpOivtOaYjjoNCg0KDQoNCuS4quS6uumFjee9rjoNCg0KYFBST1hZX1NFUlZFUl9VUkxgOiBJUOS7o+eQhuWVhuaPkOS+m+eahOebtOi/nklQ55qEQVBJ5Zyw5Z2AOw0KDQpgVEVTVF9QQUdFYDog55So5LqO5rWL6K+VSVDku6PnkIbllYbmj5DkvpvnmoRJUOS7o+eQhuacjeWKoeaYr+WQpuiDveiuv+mXrkpE55qE5rWL6K+V5Zyw5Z2ALCDlj6/ku6XmjInpnIDmm7TmjaLkuI3lkIznmoTkuqzkuJzllYblk4HlnLDlnYA7DQoNCmBtb25nb2RiYDog6YWN572u5pe26ZyA6KaB5Lul5a2X5YW45b2i5byP6YWN572uDQoNCi0gYGhvc3RgOiBNb25nb0RC5pyN5Yqh5Zmo55qE5Zyw5Z2AOw0KDQotIGBwb3J0YDogTW9uZ29EQuacjeWKoeWZqOaatOmcsueahOerr+WPozsNCg0KLSBgdXNlcm5hbWVgOiBNb25nb0RC6KaB5L2/55So55qE55So5oi35ZCNKOS5n+aYr+aVsOaNruW6k+WQjSkNCg0KLSBgcGFzc3dvcmRgOiBNb25nb0RC5a+55bqU55So5oi35ZCN55qE6aqM6K+B5a+G56CBDQoNCmBjb29raWVzYDog5rWP6KeI5Zmo6K6/6Zeu5Lqs5Lic5pe255qEYGNvb2tpZXNgLCDmnInliqnkuo7mqKHmi5/mtY/op4jlmajorr/pl64sIOmZjeS9juWksei0peeOhw0KDQoNCg0Kc2NyYXB5X3JlZGlz6YWN572uOg0KDQpgRE9OVF9GSUxURVJgOiBSZWRpc+iuv+mXruS4jeWOu+mHjSwgVHJ1ZeS7o+ihqOS4jeWOu+mHjS4g5LiN5Y676YeN55qE5Y6f5Zug5piv5L2/55So5Luj55CG6K6/6Zeu5ZWG5ZOB6K+m5oOF6aG15pe25Y+v6IO95Lya5aSx6LSlLCDmraTml7bkvJrlsIZVUkzph43mlrDmlL7lm55SZWRpc+eahOWIl+ihqOW8gOWktDsNCg0KYFNDSEVEVUxFUj0nc2NyYXB5X3JlZGlzLnNjaGVkdWxlci5TY2hlZHVsZXInYDog5L2/55Soc2NyYXB5X3JlZGlz5Lit55qE6LCD5bqm5ZmoLCDljbPkv53or4Hmr4/lj7DkuLvmnLrniKzlj5bnmoRVUkzlnLDlnYDpg73kuI3lkIznmoRTY2hlZHVsZXIuDQoNCmBEVVBFRklMVEVSX0NMQVNTID0gJ3NjcmFweV9yZWRpcy5kdXBlZmlsdGVyLlJGUER1cGVGaWx0ZXInYDog6YWN572uc2NyYXB55L2/55So55qE5Y676YeN57G7LCDljbNSRlBEdXBlRmlsdGVyLg0KDQogYFNDSEVEVUxFUl9TRVJJQUxJWkVSID0gInNjcmFweV9yZWRpcy5waWNrbGVjb21wYXQiYDog5L2/55SoYHNjcmFweV9yZWRpcy5waWNrbGVjb21wYXRg5L2c5Li65bqP5YiX5YyWLCDkuI3luo/liJfljJbnmoTmlbDmja7lj6/og73kvJrlh7rnjrDkubHnoIEuDQoNCsKgwqDCoGBBVVRPVEhST1RUTEVfRU5BQkxFRCA9IFRydWVgOiDlvIDlkK/oh6rliqjpmZDpgJ8sIOaOp+WItueIrOWPlumAn+W6pu+8jOmZjeS9juWvueaWueacjeWKoeWZqOWOi+WKmy4NCg0KIyMjIyBpdGVtcy5weQ0KDQrku6PnoIHlpoLkuIs6DQoNCmBgYHB5dGhvbg0KaW1wb3J0IHNjcmFweQ0KDQoNCmNsYXNzIEpkc2t1c3BpZGVySXRlbShzY3JhcHkuSXRlbSk6DQogICAgIyBza3UtaWQNCiAgICBza3UgPSBzY3JhcHkuRmllbGQoKQ0KICAgICMgdGl0bGUNCiAgICB0aXRsZSA9IHNjcmFweS5GaWVsZCgpDQogICAgIyDmmL7ljaHlnovlj7cNCiAgICBtb2RlbCA9IHNjcmFweS5GaWVsZCgpDQogICAgIyDmmL7lrZjnsbvlnosNCiAgICBtZW1vcnlUeXBlID0gc2NyYXB5LkZpZWxkKCkNCiAgICAjIOaYvuWtmOS9jeWuvQ0KICAgIGJpdFdpZHRoID0gc2NyYXB5LkZpZWxkKCkNCiAgICAjIOaYvuWtmOWuuemHjw0KICAgIG1lbW9yeUNhcGFjaXR5ID0gc2NyYXB5LkZpZWxkKCkNCmBgYA0KDQppdGVt5piv54is6Jmr5pWw5o2u55qE5a655ZmoLCDmlrnkvr/miJHku6zmjInlr7nlupTlrZfmrrXlrZjlgqjmlbDmja4NCg0KDQoNCiMjIyMgamRza3UucHkNCg0K5ZWG5ZOB6aG154is6Jmr55qE56iL5bqP5YWl5Y+jLCDmoLnmja5yZWRpc+aVsOaNruW6k+WvueW6lOeahOWIl+ihqOiuv+mXruWvueW6lFVSTCwg5Zyo5Lqs5Lic5ZWG5ZOB6K+m5oOF6aG16Z2i54is5Y+W6aG16Z2i5YWD57SgLiDlm6DkuLrkvb/nlKhgc2NyYXB5X3JlZGlzYOahhuaetiwg5Lmf5bCx5pivYFNjcmFweWDnmoRSZWRpc+WIhuW4g+W8j+eJiOacrCwg5Zug5q2k5aSn6YOo5YiG5Luj56CB6YO95piv5Zyo6YeN5YaZ5pa55rOVLg0KDQrlr7nkuo5gY2xhc3MgSmRza3VTcGlkZXIoUmVkaXNTcGlkZXIpYDoNCg0KKirlsZ7mgKcqKjoNCg0KYG5hbWVgOiDniKzomavnmoTlkI3lrZcsIOWQjue7reWcqOe7iOerr+WQr+WKqOeIrOiZq+aXtumcgOimgei+k+WFpeeahOeIrOiZq+WQjTsNCg0KYGFsbG93ZWRfZG9tYWluc2A6IOi/kOihjOeIrOWPlueahFVSTOiMg+WbtDsNCg0KYHJlZGlzX2tleWA6IFJlZGlz5pWw5o2u5bqT5Lit5a2Y5pS+6KaB54is5Y+W55qEVVJM55qE5YiX6KGo55qES2V55ZCNLCDkvJrojrflj5bliJfooajkuK3nmoTnrKzkuIDkuKrmlbDmja47DQoNCmByZWRpc19jb25uZWN0aW9uYDog5a2Y5pS+5LiA5Liq5paw55qEUmVkaXPov57mjqUsIOivpei/nuaOpeWQjue7reeUqOS6juiuv+mXruWksei0peaXtumHjeaWsOaKilVSTOi/lOWbnue7mVJlZGlz5YiX6KGoOw0KDQpgbW9uZ29fY29ubmVjdGlvbmA6IOWtmOaUvuS4gOS4quaWsOeahE1vbmdvRELov57mjqUsIOivpei/nuaOpeWQjue7reeUqOS6juWtmOaUvuacgOe7iOaVsOaNri4NCg0KKirmlrnms5UqKjoNCg0K5Lik5Liq5pa55rOV6YO95piv6YeN5YaZ5pa55rOVLCDmlrnms5XlkI3lkozlj4LmlbDpg73lm7rlrposIOS4pOS4quaWueazlemDveaYr+iHquWKqOiwg+eUqC4NCg0KYG1ha2VfcmVxdWVzdHNfZnJvbV91cmwoc2VsZiwgdXJsKWA6IA0KDQotIGBzY3JhcHlfcmVkaXNg5qGG5p625Lya6LCD55So6K+l5pa55rOVLCDlj5HotbfkuIDkuKrorr/pl67lj4LmlbDph4xVUkznmoRyZXF1ZXN0LCDov5Tlm57kuIDkuKpyZXNwb25zZSwg5qGG5p625Lya5bCGcmVzcG9uc2Xlj5Hnu5nku5bnmoTlm57osIPlh73mlbDlpITnkIYo5Y2z5LiL6L+w55qEYHBhcnNlYCk7DQoNCi0gYG1ldGE9eyd1cmwnOnVybH1g6KGo56S65Lya5bCGYHsndXJsJzogdXJsfWDkvZzkuLrmlbDmja7kvKDpgJLkuIvljrssIOi/meagt+WcqOS7lueahOWbnuiwg+WHveaVsChgcGFyc2VgKemHjOWwseiDveS9v+eUqOWIsOivpeaVsOaNri4NCg0KYHBhcnNlKHNlbGYsIHJlc3BvbnNlKWA6DQoNCi0gYHNjcmFweV9yZWRpc2Dlj5HpgIHnmoTor7fmsYLlvpfliLByZXNwb25zZeWQjueahOWbnuiwg+WHveaVsDsNCg0KLSDkvZznlKjmmK/op6PmnpDllYblk4Hor6bmg4XpobUo6ZKI5a+554m55a6a5ZWG5ZOBKSwg5o+Q5Y+W5pWw5o2uLCDlubblsIblhbbkv53lrZjliLBNb25nb0RC5LitLCDoi6Xmj5Dlj5bmlbDmja7ooajnjrDlh7rmnaXnmoTmmK/ooqvkuqzkuJzmi6bmiKosIOmCo+S5iOaKiuivpeasoeiuv+mXrueahFVSTOmHjeaWsOi/lOWbnuWIsFJlZGlz5YiX6KGo5byA5aS0Lg0K \ No newline at end of file +# 京东显卡商品信息分布式爬虫 + +### 介绍 + +本爬虫分两个部分: + +- 第一部分搜索页爬虫是使用selenium爬取搜索页, 主要获取的是商品的sku和价格 + +- 第二部分商品页爬虫使用scrapy爬取详情页的商品介绍信息, 主要获取商品的型号 + +由于京东商品种类繁多, 不同的商品品类间差异巨大, 商品页爬虫的代码也就不近相同. 出于成本和时间考虑, 搜索页爬虫使用**3080**作为搜索关键词, 商品页爬虫爬取搜索页爬虫获取的商品对应的详情页 + +其整体架构如下: + +![](D:\python_class\jd-distributed-crawler\img\2022-04-15-16-28-10-image.png) + +### 电商网站筛选 + +爬虫的目的就是为了获取一定量的有价值的信息, 就目前中文互联网来说, 电商网站是信息密度和价值密度比较高的地方, 因此使用爬虫获取信息有一定的可用性. + +目前而言, 淘宝获取的商品信息结果层次最多, 品类最全. 但是首先淘宝商品搜索页混杂天猫,天猫国际, 淘宝卖家等各类各不相同的店铺, 因此其商品详情页结构也大相径庭; 其次是淘宝历史包袱比较重, 因此有许多结构混乱的页面混杂其中, 难以提取数据. + +因此, 实际考虑后确认, 淘宝不是太适合新人小团队去学习试炼爬虫爬取信息. + +相对而言, 京东搜索页基本同一为京东自己的页面, 京东自营, 旗舰店和商家店结构基本没有区别, 详情页结构也一致, 对爬虫比较友好(京东唯一不太友好的地方只有, 其主站页面下并没有写robots.txt). 因此考虑爬取京东. + +### 搜索页爬虫 + +主要由`settings.yaml`和`jdSearchSeleniumSpider.py`两部分组成,`settings.yaml`是配置文件, `jdSearchSeleniumSpider.py`是代码执行主体 + +#### 前期工作 + +基本而言, 任何有搜索功能的网站都会对其搜索功能做一定的限制, 以防止单个ip过高频率的访问, 占用过多资源. + +京东也不例外, 对于未登录的请求, 甚至是带着header和cookie的请求, 往往都是秉持拒绝的态度, 要求验证登录: + +![](D:\python_class\jd-distributed-crawler\img\2022-04-15-13-25-48-image.png) + +![](D:\python_class\jd-distributed-crawler\img\2022-04-15-13-26-42-image.png) + +搜索引擎的爬虫验证一般级别都比较高, 难以绕过. 考虑到搜索页的数量相对于商品页的数量往往是少一个数量级, 且京东单个搜索词的页面量也大致在几十几百页的数量级之间, 因此使用python的`selenium`库, 驱动浏览器去自动化访问搜索页, 进而获取数据, 是比较简单且可行的方案. + +#### settings.yaml + +`setting.yaml`是搜索页爬虫的配置文件, 其内容如下: + +```yaml +search: + keyword: '3080' + sleep: 5 + +redis: + host: '120.24.87.40' + port: '6379' + password: 'Guet@207' + db: 1 + +mongodb: + host: '120.24.87.40' + port: '27017' + username: "admin" + password: "123456" + +result: + rpush_key: "jd:start_urls" +``` + +- `search` + + - `keyword`: 要搜索的关键字, 即在京东商品搜索上的要搜索的商品; + + - `sleep`: 休眠时间, 单位: 秒; 由于**selenium**操作页面的动作(例如点击翻页, 下拉)是**异步**的, 而读取页面是**同步**的. 京东需要执行下拉这个操作后才加载出所有的商品信息, 为了读取全当前的页面信息, 需要休眠一段时间等待异步操作完成; + +- `redis` + + - `host`: redis部署的服务器地址; + + - `port`: redis部署时开放的端口号; + + - `password`: redis部署时设置的auth; + + - `db`: 需要使用redis的第几个数据库 + +- `mongodb` + + - `host`: MongoDB部署的服务器地址; + + - `port`: MongoDB部署时开放的端口号; + + - `username`: MongoDB部署时设置的auth的用户名, 或者说要使用的MongoDB的数据库名; + + - `password`: MongoDB部署时设置的auth. + +- `result` + + - `rpush_key`: 爬虫获取到的数据形成的url要放入redis时设置的key, url在Redis中存储方式是列表 + +#### jdSearchSeleniumSpider.py + +搜索页爬虫的程序入口, 根据京东搜索页面爬取页面元素. + +启动程序后进入京东的商品搜索页面. 搜索页的搜索关键字为`settings.py`中设定的`search.keyword`. + +京东的搜索页面会分两个阶段加载, 第一个加载阶段加载成功后(即加载出30个商品的信息), 爬虫会执行`scroll_by_xpath()`, 作用是滚动网页到网页底部, 触发京东搜索页面的第二个加载阶段, 把一个页面上的信息都加载到浏览器里. + +`scroll_by_xpath()` 内部原理就是驱动浏览器移动视图, 使视图可以看到指定的元素. 在该爬虫中, 指定的网页元素的**xpath**为`//*[@id="J_bottomPage"]/span[1]/a[9]`, 代表的是京东搜索页面的下一页按钮这一网页元素. + +两个阶段都加载完成后, 获取网页中所有商品的sku和价格. + +![](D:\python_class\jd-distributed-crawler\img\2022-04-15-18-40-23-image.png) + +sku是京东给商品的编号, 且可以根据sku拼接对应商品的详情页的URL. + +获取到sku和价格后, 将sku拼接为URL, rpush到Redis设置好的数据库的`result.rpush_key`中, 并且将每个商品的数据以`{'sku': sku, 'price': price}`的形式, 放入MongoDB设置好的数据库中. 放入MongoDB前会根据sku字段查询数据是否存在(原子操作, 可分布式), 防止重复插入. + +### 商品页爬虫 + +商品页爬虫使用了`scrapy_redis`去写, 即`Scrapy`的redis版本, 他与`Scrapy`的主要区别就是把存放URL于Reids数据库中, 利用Redis的特性, 即在多个请求请求同一个数据时, 会默认对请求排序, 有一个队列效果, 保证了分布式爬虫获取到不同的URL. + +`scrapy_redis`框架下的爬虫执行是异步的, 即, 从Redis数据库的列表中获取URL后, 发起Request, 便可以执行下一次从Redis中获取Redis, 而发起Request得到的response则等得到后再由回调函数处理(即执行`jdsku.py`里的`parse`方法). + +该爬虫只需要在**本爬虫目录下**, 调用`scrapy crawl jdsku`即可启动. + +#### settings.py + +配置文件, 具体配置查看`Scrapy`的配置, 其中有几个是`scrapy_redis`的配置或本次项目引入的配置, 在此说明: + + + +个人配置: + +`PROXY_SERVER_URL`: IP代理商提供的直连IP的API地址; + +`TEST_PAGE`: 用于测试IP代理商提供的IP代理服务是否能访问JD的测试地址, 可以按需更换不同的京东商品地址; + +`mongodb`: 配置时需要以字典形式配置 + +- `host`: MongoDB服务器的地址; + +- `port`: MongoDB服务器暴露的端口; + +- `username`: MongoDB要使用的用户名(也是数据库名) + +- `password`: MongoDB对应用户名的验证密码 + +`cookies`: 浏览器访问京东时的`cookies`, 有助于模拟浏览器访问, 降低失败率 + + + +scrapy_redis配置: + +`DONT_FILTER`: Redis访问不去重, True代表不去重. 不去重的原因是使用代理访问商品详情页时可能会失败, 此时会将URL重新放回Redis的列表开头; + +`SCHEDULER='scrapy_redis.scheduler.Scheduler'`: 使用scrapy_redis中的调度器, 即保证每台主机爬取的URL地址都不同的Scheduler. + +`DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'`: 配置scrapy使用的去重类, 即RFPDupeFilter. + + `SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"`: 使用`scrapy_redis.picklecompat`作为序列化, 不序列化的数据可能会出现乱码. + +   `AUTOTHROTTLE_ENABLED = True`: 开启自动限速, 控制爬取速度,降低对方服务器压力. + +#### items.py + +代码如下: + +```python +import scrapy + + +class JdskuspiderItem(scrapy.Item): + # sku-id + sku = scrapy.Field() + # title + title = scrapy.Field() + # 显卡型号 + model = scrapy.Field() + # 显存类型 + memoryType = scrapy.Field() + # 显存位宽 + bitWidth = scrapy.Field() + # 显存容量 + memoryCapacity = scrapy.Field() +``` + +item是爬虫数据的容器, 方便我们按对应字段存储数据 + + + +#### jdsku.py + +商品页爬虫的程序入口, 根据redis数据库对应的列表访问对应URL, 在京东商品详情页面爬取页面元素. 因为使用`scrapy_redis`框架, 也就是`Scrapy`的Redis分布式版本, 因此大部分代码都是在重写方法. + +对于`class JdskuSpider(RedisSpider)`: + +**属性**: + +`name`: 爬虫的名字, 后续在终端启动爬虫时需要输入的爬虫名; + +`allowed_domains`: 运行爬取的URL范围; + +`redis_key`: Redis数据库中存放要爬取的URL的列表的Key名, 会获取列表中的第一个数据; + +`redis_connection`: 存放一个新的Redis连接, 该连接后续用于访问失败时重新把URL返回给Redis列表; + +`mongo_connection`: 存放一个新的MongoDB连接, 该连接后续用于存放最终数据. + +**方法**: + +两个方法都是重写方法, 方法名和参数都固定, 两个方法都是自动调用. + +`make_requests_from_url(self, url)`: + +- `scrapy_redis`框架会调用该方法, 发起一个访问参数里URL的request, 返回一个response, 框架会将response发给他的回调函数处理(即下述的`parse`); + +- `meta={'url':url}`表示会将`{'url': url}`作为数据传递下去, 这样在他的回调函数(`parse`)里就能使用到该数据. + +`parse(self, response)`: + +- `scrapy_redis`发送的请求得到response后的回调函数; + +- 作用是解析商品详情页(针对特定商品), 提取数据, 并将其保存到MongoDB中, 若提取数据表现出来的是被京东拦截, 那么把该次访问的URL重新返回到Redis列表开头.