Тема: AHK: Пример скачивания записи ЖЖ с развёрнутыми комментариями
Скрипт скачивает запись ЖЖ с подстраницами комментариев (не переходя по трэдам — свёрнутым комментариям) и извлекает запись и информацию о смещении каждого комментария —
https://miss-tramell.livejournal.com/1564043.html?page=%pg_num%&style=mine
* [Поскольку от журнала к журналу стили имеют разный код, скачивание должно происходить в стиле одного журнала (style=mine), через который скрипт логинится в ЖЖ. При этом комментарии в его стиле должны иметь не JS, а HTML код].
Затем загружаются комментарии в JSON-виде (доступные, даже когда владелец журнала закрыл их отображение у себя) —
https://www.livejournal.com/__rpc_get_thread?journal=miss-tramell&itemid=1564043&flat=&skip=&media=&expand_all=1&page=%pg_num%
парсятся и объединяются с записью и инфой о смещениях комментов, в итоге получая развёрнутые кооментарии сплошным текстом.
#MaxMem 500
SetBatchLines -1
#SingleInstance Force
;----------------------------------------------------------------------
proxy := "109.207.62.69:8080"
ProxyOrNot:=0 ; задействовать прокси, или нет
LoginOrNot:=1 ; логиниться, или нет
ComObjError(false)
HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
login := "**********"
password := "**********"
path = %A_ScriptDir%\
SplashBC = 00B0EA
;----------------------------------------------------------------------
loop_link = https://miss-tramell.livejournal.com/1564043.html
RegExMatch(loop_link, "s)(https?://(.*?)\.livejournal\.com/)?(\d+)\.html(\?style=mine)?", match)
lj_uname := match2
lj_pgnum := match3
dest_path = %path%%lj_pgnum%.html
if (NOT FileExist(dest_path))
Gosub, dwn
else
{
msgbox Файл существует`n%dest_path%
ExitApp
}
dwn:
If not (LoginMode == 1) ;при первом запуске один раз:
{
If LoginOrNot ;если надо логиниться
GoSub, login
LoginMode = 1
}
; скачивание записи с подстраницами комментариев
n := 1, LJ_Tmp_Pages :="", cmtCount :=""
;HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
loop
{
HTTP.Open("GET", "https://" lj_uname ".livejournal.com/" lj_pgnum ".html?page=" n "&style=mine", true)
If ProxyOrNot
HTTP.SetProxy(2, proxy)
GoSub, SetRequestHeader
HTTP.Send()
HTTP.WaitForResponse()
ResponseText := HTTP.ResponseText
LJ_Tmp_Pages .= "`n" ResponseText
If A_Index = 1
{
cmtCount := RegExReplace(ResponseText, "s)^.*<a [^>]*\.html\?page=(\d+)(&style=mine)?(#comments)?'>(<b>)?(\[)?\1(\])?(</b>)?</a>.*?$", "$1")
LjPost := RegExReplace(ResponseText, "s).*?(<div id=""content"">.*?)<div class=""quickreply"" id=""ljqrtentrycomment"".*", "$1")
LjPost := RegExReplace(LjPost, "s)^(.*<div class=""asset-tags"">.*?</div>).*", "$1</div>`n</div>`n</div>`n</div>`n</div>`n</div>`n</div>`n</div>`n</div>`n")
PostTitle := RegExReplace(ResponseText, "s).*?<title>(.*?)</title>.*", "$1")
PostTitle := RegExReplace(PostTitle, "\R+\s*|\s+$", "")
If NOT InStr(ResponseText, "id=""ljcmt") or NOT ResponseText~="\.html\?page=\d+&style=mine?(#comments)?'><b>\[\d+\]</b>"
break
}
if (n >= cmtCount)
break
n+=1 ; смещение по offset
}
ResponseText =
CmtThreads =
loop
{
;скачивание страниц комментов в JSON формате
get_thread := "https://www.livejournal.com/__rpc_get_thread?journal=" lj_uname "&itemid=" lj_pgnum "&flat=&skip=&media=&expand_all=1&page=" A_Index
HTTP.Open("GET", get_thread, true)
GoSub, SetRequestHeader
HTTP.Send()
HTTP.WaitForResponse()
ResponseText := HTTP.ResponseText
StrReplace(ResponseText, "{""shown"":",, CmtCountFromPage)
If A_Index = 1
cmtCountAll := RegExReplace(ResponseText, "s)^\{""replycount"":(\d+).*$", "$1")
If NOT InStr(ResponseText, "{""replycount"":") or (CmtCountFromPage = 0)
break
Gosub, JSON_Parse
CountFromPage += CmtCountFromPage
ResponseText =
If (CountFromPage > cmtCountAll)
break
}
CmtThreads := RegExReplace(CmtThreads, "s)<a [^>]*><img [^>]*></a>\n<div [^>]*><img [^>]*> .<a title="""" href=""""></a> – <a [^>]*></a> .</div>\n(<div [^>]*><b>\(Удалённый комментарий\)</b></div>)", "$1")
match := "", Pos := 1
While ( Pos := RegExMatch(LJ_Tmp_Pages, "s)(<div\s+id=""ljcmt)(\d+)("" style=""margin-left: ?\d+px;?"")", match, Pos + StrLen(match)) )
StringReplace, CmtThreads, CmtThreads, <div id="ljcmt%match2%", %match1%%match2%%match3%, All
GoSub, Sklonenie
Gosub, htmlcode
fileappend, %htmlcode%`n`n%LjPost%`n`n`n, %dest_path%, UTF-8
fileappend, <center><h2>%CountFromPage%</h2></center>`n`n`n%CmtThreads%`n`n</body>`n</html>`n`n, %dest_path%, UTF-8
CountFromPage =
cmtCountAll =
CmtCountFromPage =
LjPost =
PostTitle =
cmtCount =
lj_pgnum =
lj_uname =
LJ_Tmp_Pages =
CmtThreads =
ResponseText =
LJ_Tmp_Pages =
match =
match1 =
match2 =
match3 =
match4 =
loop_link =
str =
unit =
return
Sklonenie:
unit := CountFromPage - (CountFromPage // 100 * 100)
unit > 20 ? dPart := unit // 10 * 10 : dPart := 0
str := (unit == 1 + dPart) ? "комментарий" : (unit > 1 + dPart && unit < 5 + dPart) ? "комментария" : "комментариев"
CountFromPage := CountFromPage " " str
return
JSON_Parse:
obj := JSON.Parse(ResponseText)
keys =
(
userpic
uname
commenter_journal_base
dtalkid
upictitle
article
ctime
deleted
parent
subject
striked
userhead_url
dname
)
for k, v in obj.comments {
Loop, parse, keys, `n, `r
%A_LoopField%%k% := SearchKey(v, A_LoopField)
if (userpic%a_index% = "")
userpic%a_index% := "https://l-stat.livejournal.net/img/userpics/userpic-user.png?v=15821"
if (striked%a_index% = "1")
dname%a_index% := "<s>" dname%a_index% "</s>"
if (deleted%a_index% = "1")
{
userpic%a_index% =
article%a_index% := "<b>(Удалённый комментарий)</b>"
}
ctime%a_index% := RegExReplace(ctime%a_index%, "s) UTC", "")
CmtThreads .= "`n<div id=""ljcmt" dtalkid%a_index% """ class=""comment""><a name=""" dtalkid%a_index% """></a>`n<a href=""#" parent%a_index% """><img class=""img"" title=""" upictitle%a_index% """ src=""" userpic%a_index% """></a>`n<div class=""info""><img class=""userhead_url"" src=""" userhead_url%a_index% """> <a title=""" uname%a_index% """ href=""" commenter_journal_base%a_index% """>" dname%a_index% "</a> – <a href=""https://" lj_uname ".livejournal.com/" lj_pgnum ".html?thread=" dtalkid%a_index% "#t" dtalkid%a_index% """>" ctime%a_index% "</a> " subject%a_index% "</div>`n<div class=""content"">" article%a_index% "</div>`n</div>`n"
}
return
login:
SplashImage,, y-2 x60 w40 h14 M C11 ZH0 ZW0 ZX1 ZY1 B1 CTffffff CW%SplashBC% FM8 FS6 WM600 WS400,, login
;--------------------------------------------
;Авторизация:
;--------------------------------------------
HTTP.Open("GET", "https://www.livejournal.com/login.bml", true)
If ProxyOrNot
HTTP.SetProxy(2, proxy)
GoSub, SetRequestHeader
HTTP.Send()
HTTP.WaitForResponse()
RegexMatch(HTTP.ResponseText, "s)name=""lj_form_auth"" value=""([^""]*)""", match)
PostData := "lj_form_auth=" match1 "&ref=&returnto=%2F&user=" login "&password=" password "&remember_me=1&action%3Alogin="
StringReplace, PostData, PostData, :, `%3A, All
HTTP.Open("POST", "https://www.livejournal.com/login.bml", true)
If ProxyOrNot
HTTP.SetProxy(2, proxy)
HTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
GoSub, SetRequestHeader
HTTP.Send(PostData)
HTTP.WaitForResponse()
If !Instr(HTTP.ResponseText, "-->Выйти<!--")
; msgbox Log On!
;Else
msgbox, LogOn Failed!
ResponseText =
;--------------------------------------------
SplashImage, OFF
Return
SetRequestHeader:
HTTP.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko)")
HTTP.SetRequestHeader("Pragma", "no-cache")
HTTP.SetRequestHeader("Cache-Control", "no-cache, no-store")
HTTP.SetRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT")
return
htmlcode:
htmlcode=
(
<!DOCTYPE html>
<html>
<head>
<title>%PostTitle%</title>
<meta http-equiv=Content-Type content="text/html; charset=UTF-8">
<style type="text/css"> <!--
body {
Xwidth: 100`%;
margin-bottom: 10`% !important;
}
.comment {
Xbackground-color:#EEEEEE;
padding: 20px 20px 0px 20px;
}
.content {
width: 95`%;
padding: 10px 20px 20px 60px;
}
.comment .img {
float: left;
width: 60px;
Xheight: 60px;
margin: 3px 10px 5px 0px;
}
.info {
color:#767676;
font-size:90`%;
}
.info a {
font-weight:bold;
}
--></style>
<link rel="stylesheet" type="text/css" href="https://l-stat.livejournal.net/??voxhtml/base.css,voxhtml/default/screen.css,voxhtml/woodcut/screen.css,voxhtml/ljextras.css,voxhtml/widget-threeposts.css,voxhtml/widget-ramblerpartner.css?v=1530780559" media="all">
</head>
<body>
`n`n`n`n
)
Return
SearchKey(obj, key) {
for k, v in obj {
if (k = key)
Return v
if IsObject(v) {
res := SearchKey(v, key)
if (res != "")
Return res
}
}
}
class JSON
{
static JS := JSON._GetJScripObject()
Parse(JsonString) {
try oJSON := this.JS.("(" JsonString ")")
catch {
MsgBox, Wrong JsonString!
Return
}
Return this._CreateObject(oJSON)
}
_GetJScripObject() {
VarSetCapacity(tmpFile, (MAX_PATH := 260) << !!A_IsUnicode, 0)
DllCall("GetTempFileName", Str, A_Temp, Str, "AHK", UInt, 0, Str, tmpFile)
FileAppend,
(
<component>
<public><method name='eval'/></public>
<script language='JScript'></script>
</component>
), % tmpFile
JS := ObjBindMethod( ComObjGet("script:" . tmpFile), "eval" )
FileDelete, % tmpFile
JSON._AddMethods(JS)
Return JS
}
_AddMethods(ByRef JS) {
JScript =
(
Object.prototype.GetKeys = function () {
var keys = []
for (var k in this)
if (this.hasOwnProperty(k))
keys.push(k)
return keys
}
Object.prototype.IsArray = function () {
var toStandardString = {}.toString
return toStandardString.call(this) == '[object Array]'
}
)
JS.("delete ActiveXObject; delete GetObject;")
JS.(JScript)
}
_CreateObject(ObjJS) {
res := ObjJS.IsArray()
if (res = "")
Return ObjJS
else if (res = -1) {
obj := []
Loop % ObjJS.length
obj[A_Index] := this._CreateObject(ObjJS[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := ObjJS.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(ObjJS[k])
}
Return obj
}
}