PDA

View Full Version : سوال: چطور می شه با RegEx تگ های داخل تگ Head از Html رو داشت؟



ROSTAM2
جمعه 17 شهریور 1402, 01:41 صبح
من یک کد HTML دارم اول می خوام تگ Head رو از رشته جدا کنم بعد هم تگ های Link که بتونم آدرس favicon رو بدست بیارم. جواب می خوام و اگه مسیر ساده تری سراغ دارید ممنون می شم پاسخ بدید

ROSTAM2
جمعه 17 شهریور 1402, 11:53 صبح
سلام مجدد
کدی که من دارم اینه ولی فقط اطلاعات سایت آپارات رو برای من برمی گردون و سایتای خارجی رو جوابگو نیست !!! راه حل چیه؟!

از متود PreserveWeb استفاده کنید و پنجره Debug رو تماشا کنید....
قسمت اول سایز آیکون سایت هست و قسمت دوم آدرسش تو هاست.

154910


Imports System.Net


Module Methods
Dim WithEvents client As New WebClient()
Dim Match As New System.Text.RegularExpressions.Regex("<([a-z]+)(?![^>]*\/>)[^>]*>")
'"([^"]*)"
Dim AttribPattern As New System.Text.RegularExpressions.Regex(String.Format ("\w*\=\{0}([^{0}]*)\{0}", Chr(34)))
Class AttributeInfo
Public Name, Value As String
Sub New(Name$, Value$)
Me.Name = Name
Me.Value = Value
End Sub
End Class
Function Attribute(input As String) As AttributeInfo
If input.Length = 0 Then Return Nothing
Dim spl As String() = input.Split("=")
If input.Contains("=") = True Then
If spl.Length = 2 Then
Return New AttributeInfo(spl(0), spl(1).Replace(Chr(34), ""))
End If
End If
Return Nothing
End Function


Dim IconCollection As New Dictionary(Of Size, String)
Dim isShortcutIcon As Boolean = False
Private Sub client_DownloadStringCompleted(sender As Object, e As DownloadStringCompletedEventArgs) Handles client.DownloadStringCompleted
Dim Attrib As AttributeInfo
Dim SplSize As String()
Dim S As Size = New Size(0, 0)
Dim PATH As String = ""
Try
'Debug.Print(e.Result)
For Each M As System.Text.RegularExpressions.Match In Match.Matches(e.Result)
If M.Success = True Then
If M.Value.Trim.StartsWith("<link") = True And M.Value.Trim.EndsWith(">") Then
'Debug.Print(M.Value)
For Each Attr As System.Text.RegularExpressions.Match In AttribPattern.Matches(M.Value)
If Attr.Success Then
' Debug.Print(Attr.Value)
Attrib = Attribute(Attr.Value)
If Attrib.Name.ToLower = "rel" AndAlso Attrib.Value.ToLower = "shortcut icon" Then
isShortcutIcon = True
End If
If isShortcutIcon = True Then
If Attrib.Name.ToLower = "sizes" Then
SplSize = Attrib.Value.Split("x")
S = New Size(SplSize(0), SplSize(1))
End If
If Attrib.Name.ToLower = "href" Then
PATH = Attrib.Value
End If
End If
End If
Next
If isShortcutIcon = True Then
IconCollection.Add(S, PATH)
Debug.Print(S.ToString + ": " + PATH)
isShortcutIcon = False
End If
End If
End If
Next
Catch ex As Exception
Debug.Print(ex.Message)
End Try
End Sub


Sub PreserveWeb(Address As String)
Dim url As Uri = New Uri(Address)
If url.HostNameType = UriHostNameType.Dns Then
Dim URa = String.Format("{0}://{1}/", url.Scheme, url.Host)
client.DownloadStringAsync(New Uri(URa))
End If
End Sub
End Module

ROSTAM2
جمعه 17 شهریور 1402, 12:28 عصر
خوب این یکی کد فکر کنم بهتر باشه فقط برای بدست آوردن آیکون:


Dim WithEvents WB As New WebBrowser With {.ScriptErrorsSuppressed = True}
Sub PreserveWeb(Address As String)


Dim url As Uri = New Uri(Address)
If url.HostNameType = UriHostNameType.Dns Then
Dim URa = String.Format("{0}://{1}/", url.Scheme, url.Host)
'client.DownloadStringAsync(New Uri(URa))
WB.Navigate(URa)
Do Until WB.ReadyState = WebBrowserReadyState.Complete
Application.DoEvents()
Loop
Dim DomDoc As mshtml.IHTMLDocument3 = WB.Document.DomDocument
With DomDoc
For Each link As mshtml.HTMLLinkElement In .getElementsByTagName("link")
Debug.Print(link.outerHTML)
'If link.getAttribute("rel").ToString.ToLower = "shortcut icon" Then
' Debug.Print(link.getAttribute("href"))
'End If
Next
End With
End If
End Sub

ROSTAM2
جمعه 17 شهریور 1402, 15:11 عصر
خوب بالاخره تونستم با این روش اکثریت آیکون سایتا رو بدست بیارم اما هنوز ی سایتی مث برنامه نویس خطا بر می گردونه:

Imports System.Net
Imports System.Windows.Forms


Dim WithEvents WB As New WebBrowser With {.ScriptErrorsSuppressed = True}
Dim WithEvents client As New WebClient()



Sub PreserveWeb(Address As String)
Dim url As Uri = New Uri(Address)
If url.HostNameType = UriHostNameType.Dns Then
Dim URa = String.Format("{0}://{1}/", url.Scheme, url.Host)
'client.DownloadStringAsync(New Uri(URa))
WB.Navigate(URa)
End If
End Sub


Private Sub WB_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WB.DocumentCompleted
Dim DomDoc As mshtml.IHTMLDocument3 = WB.Document.DomDocument
IconLoaded = False
With DomDoc
Try
For Each link As mshtml.HTMLLinkElement In .getElementsByTagName("link")
' Debug.Print(link.outerHTML)
If IconLoaded = True Then Exit For
If IconLoaded = False AndAlso (link.getAttribute("rel").ToString.ToLower = "icon" Or link.getAttribute("rel").ToString.ToLower = "shortcut icon") Then
Debug.Print(link.getAttribute("href"))
Debug.Print(e.Url.OriginalString)
Dim urlIcon As String
If link.getAttribute("href").ToString.Contains(e.Url.Host) = True Then
urlIcon = link.getAttribute("href").ToString
Else
urlIcon = String.Format("{0}{1}", String.Format("{0}://{1}/", e.Url.Scheme, e.Url.Host), link.getAttribute("href").ToString)
End If
Debug.Print(urlIcon)
client.DownloadDataAsync(New Uri(urlIcon))
End If
Next
Catch ex As Exception
Debug.Print(ex.Message)
End Try
End With
End Sub
Dim Strm As IO.Stream
Dim IconLoaded As Boolean = False
Private Sub client_DownloadDataCompleted(sender As Object, e As DownloadDataCompletedEventArgs) Handles client.DownloadDataCompleted
Debug.Print(e.Result.Length)
Strm = New IO.MemoryStream(e.Result)
' Dim bmp As Bitmap = Image.FromStream(Strm)
Strm.Position = 0
ItemDialog.Icon = New Icon(Strm) 'bmp.
Strm.Dispose()
IconLoaded = CBool(e.Result.Length > 0)
End Sub

ROSTAM2
جمعه 17 شهریور 1402, 18:44 عصر
Imports System.ComponentModel
Imports System.Net



Dim WithEvents WB As New WebBrowser With {.ScriptErrorsSuppressed = True}
Dim WithEvents client As New WebClient()



Sub PreserveWeb(Address As String)
Dim url As Uri = New Uri(Address)
If url.HostNameType = UriHostNameType.Dns Then
Dim URa = String.Format("{0}://{1}/", url.Scheme, url.Host)
'client.DownloadStringAsync(New Uri(URa))
WB.Navigate(URa)
End If
End Sub


Private Sub WB_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WB.DocumentCompleted
Dim DomDoc As mshtml.IHTMLDocument3 = WB.Document.DomDocument
IconLoaded = False
With DomDoc
Try
'<meta Property= "og:image" content="https://learn.microsoft.com/en-us/media/open-graph-image.png" />
Dim href As String = ""
For Each link As mshtml.HTMLLinkElement In .getElementsByTagName("link")
' Debug.Print(link.outerHTML)
If IconLoaded = True Then Exit For
If IconLoaded = False AndAlso (link.getAttribute("rel").ToString.ToLower = "icon" Or link.getAttribute("rel").ToString.ToLower = "shortcut icon") Then
Debug.Print(link.getAttribute("href"))
Debug.Print(e.Url.OriginalString)
Dim urlIcon As String
If link.getAttribute("href").ToString.Contains(e.Url.Host) = True Then
urlIcon = link.getAttribute("href").ToString
Else
href = link.getAttribute("href").ToString
If href.StartsWith("/") = False Then
href = href.Insert(0, "/")
End If
urlIcon = String.Format("{0}{1}", String.Format("{0}://{1}", e.Url.Scheme, e.Url.Host), href).Replace(".com//", ".com/")
End If
Debug.Print(urlIcon)
client.DownloadDataAsync(New Uri(urlIcon))
End If
Next
For Each link As mshtml.HTMLMetaElement In .getElementsByTagName("meta")
If IconLoaded = True Then Exit For
If IconLoaded = False AndAlso (link.getAttribute("Property").ToString.ToLower = "og:image") Then
Debug.Print(link.content)
Debug.Print(e.Url.OriginalString)
Dim urlIcon As String
If link.content.Contains(e.Url.Host) = True Then
urlIcon = link.content
Else
href = link.content
If href.StartsWith("/") = False Then
href = href.Insert(0, "/")
End If
urlIcon = String.Format("{0}{1}", String.Format("{0}://{1}", e.Url.Scheme, e.Url.Host), href).Replace(".com//", ".com/")
End If
Debug.Print(urlIcon)
'client.DownloadDataAsync(New Uri(urlIcon))
client.DownloadFileAsync(New Uri(urlIcon), IO.Path.GetFullPath(".\favicon.png"))
End If
Next
Catch ex As Exception
Debug.Print(ex.Message)
End Try
End With
End Sub
Dim Strm As IO.Stream
Dim IconLoaded As Boolean = False
Dim Icon As Icon, Image As Image
Private Sub client_DownloadDataCompleted(sender As Object, e As DownloadDataCompletedEventArgs) Handles client.DownloadDataCompleted
Debug.Print(e.Result.Length)
Strm = New IO.MemoryStream(e.Result)
Try
Icon = New Icon(Strm)
ItemDialog.Icon = Icon
IconLoaded = CBool(e.Result.Length > 0)
Catch ex As Exception
Try
Dim bmp As New Bitmap(Strm)
Image = bmp
ItemDialog.PictureBox1.Image = Image
IconLoaded = CBool(e.Result.Length > 0)
Catch ex2 As Exception


End Try
End Try
End Sub


Private Sub client_DownloadFileCompleted(sender As Object, e As AsyncCompletedEventArgs) Handles client.DownloadFileCompleted
Try
Image = Image.FromFile(IO.Path.GetFullPath(".\favicon.png"))
ItemDialog.PictureBox1.Image = Image
IconLoaded = True
Catch ex As Exception


End Try


End Sub

mazoolagh
یک شنبه 26 شهریور 1402, 13:21 عصر
سلام و روز خوش

1- در کل استفاده از regex بعنوان parser (هر نوع parser) کار خوبی نیست؛
در حالی که مرورگرها کدهای html ناقص و نادرست رو هم render میکنن، کدهای regex نمیتونن اونها رو درست تشخیص بدن (در حالت کلی).
و هم این که به نوعی اختراع دوباره چرخ هست!
برای html parsing همین mshtml کاملا مناسبه که خب خودتون هم به همین نکته رسیدین.


2- در حالت عمومی برای web scraping نیازی به webbrowser نیست؛
چون فقط قرار هست از سورس دیتا بیرون بکشیم و با خود سایت تعاملی نداریم.
پس کافی هست به یک روشی فقط سورس رو بخونیم (مثلا با webclient یا webrequest) و بعد این سورس رو با یک parser آنالیز کنیم.
ولی بعضی از سایتها (مثل stackoverflow) جلوی این کار رو میگیرن (خیلی ساده با چک کردن header ارسالی) ،
و در این صورت باید حتما از یک کنترل مثل webbrowser (که activex هست) یا webview2 (که dotnet control) هست استفاده کنیم.
بنابراین اگر از webclient یا webrequest استفاده میکنیم باید یک فکری هم به حال user agent برداریم که دچار مشکل نشیم.


3- در سیستم های به روز شده که Internet Explorer 11 دارن، یک نسخه از mshtml جدیدتر هست که امکانات بیشتری نسبت به نسخه قدیمی (ie 7) داره.
شاید سودمندترین اونها queryselect/queryselectall باشه که یک آموزش از اون در تاپیک زیر هست:
ساخت یک دیتابیس از همه تاپیک‌ها و پست‌های تالار اکسس - Web Scraping in Access VBA (https://barnamenevis.org/showthread.php?574964)
همه ویندوزهای 10 و 11 این ورژن رو دارن.

4- در مورد دانلود آیکونها، توضیحی داده نشده ولی بسیار ساده تر میشه نوشت.

mazoolagh
جمعه 31 شهریور 1402, 12:16 عصر
در پست اصلی توضیحی در مورد معیار جستجوی favicon داده نشده، ولی گویا فرمت کلی به شکل نمونه زیر هست:

<link rel="shortcut icon" href="/favicon.ico">

<link rel="icon" sizes="16x16 32x32 64x64" href="/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">

<link rel="apple-touch-icon" href="/favicon-57.png">
<link rel="apple-touch-icon" sizes="120x120" href="/favicon-120.png">

<meta name="msapplication-TileImage" content="/favicon-144.png">


البته اینها هم گویا استفاده میشه:

<link rel="image_src" href="/favicon.ico">

<meta property="og:image" content="https://abc.com/favicon.png" />

اسم و آدرس فایل ها و اندازه ها نمونه است!

با اینحساب کافی هست موارد زیر رو بررسی کنیم:
1- تگ های link که در rel عبارت icon دارن یا rel اونها برابر image_src هست
2- تگ های meta که property اونها برابر og:image هست
3- تگ های meta که name اونها برابر msapplication-TileImage هست

بررسی اینها با متد queryselector/queryselectorall خیلی ساده است.

mazoolagh
جمعه 31 شهریور 1402, 12:27 عصر
Imports System.Text.Json
Imports Microsoft.Web.WebView2
Imports MZhtml

Private WithEvents WV2 As WinForms.WebView2
Private IconsFolder As String = "...."
Private DownloadFolder As String
Private Site_Uri As Uri
Private Navigation_Completed As Boolean

WV2 = New WinForms.WebView2
Site_Uri = New Uri("https://...")
DownloadFolder = $"{IconsFolder}\{Site_Uri.Host}"
If Not IO.Directory.Exists(DownloadFolder) Then
IO.Directory.CreateDirectory(DownloadFolder)
End If

Dim t As Task = WV2.EnsureCoreWebView2Async(Nothing)
Do Until t.IsCompleted
Application.DoEvents()
Loop

Navigation_Completed = False
WV2.CoreWebView2.Navigate(Site_Uri.ToString)
Do Until Navigation_Completed
Application.DoEvents()
Loop
WV2.Dispose()

Private Async Sub WV2_NavigationCompleted(
sender As Object,
e As Core.CoreWebView2NavigationCompletedEventArgs
) Handles WV2.NavigationCompleted
Try
Dim html As String = Await WV2.ExecuteScriptAsync("document.documentElement.outerHTML;")
html = CStr(JsonSerializer.Deserialize(html, GetType(String)))

Dim idoc2 As IHTMLDocument2 = CType(New HTMLDocument, IHTMLDocument2)
idoc2.write(html)
idoc2.close()
Dim document As HTMLDocument = CType(idoc2, HTMLDocument)

Dim links = document.querySelectorAll("link[rel*='icon'] , link[rel='image_src']")
For Each link As HTMLLinkElement In links
DownloadFile(New Uri(Site_Uri, link.href).ToString)
Next

Dim metas = document.querySelectorAll("meta[property='og:image'] , meta[name='msapplication-TileImage']")
For Each meta As HTMLMetaElement In metas
DownloadFile(New Uri(Site_Uri, meta.content).ToString)
Next

Navigation_Completed = True
Catch ex As Exception
Throw New Exception(ex.Message)
End Try
End Sub

Sub DownloadFile(File_Address As String)
If File_Address.Contains("?") Then
File_Address = File_Address.Split("?"c)(0)
End If
Try
My.Computer.Network.DownloadFile(address:=File_Add ress,
destinationFileName:=$"{DownloadFolder}\{File_Address.Split("/"c).Last}",
userName:=String.Empty,
password:=String.Empty,
showUI:=False,
connectionTimeout:=10000,
overwrite:=True)
Catch ex As Exception
Console.WriteLine($"Error downloading {File_Address}{vbCrLf}{ex.Message}")
End Try
End Sub

mazoolagh
جمعه 31 شهریور 1402, 19:41 عصر
چند نکته در مورد کدهای پست 8:
1- از کنترل WebView2 فقط برای خوندن سورس سایت استفاده شده و parse به عهده mshtml هست.

2- میشد از webclient یا httpwebrequest و ... هم برای خوندن سورس استفاده کرد،
ولی بعضی سایتها (مثل خود مایکروسافت) حتی با تغییر user agent هم میتونن اون رو از یک مرورگر واقعی تشخیص بدن!
و این که هدف آشنایی با WebView2 هم بود.

3- برخلاف کنترل قدیمی Web Browser ، در WebView2 دسترسی مستقیم به dom document نداریم؛
بنابراین مجبور شدیم که با متد ExecuteScriptAsync ، متن html رو بگیریم و از روی اون یک htmldocument بسازیم و ادامه داستان.
در پست های بعدی روش دیگه ای رو آموزش میدم.

4- برای دانلود هم از My.Computer.Network.DownloadFile استفاده شده که کدنویسی رو ساده و کوتاه میکنه.

mazoolagh
جمعه 31 شهریور 1402, 19:51 عصر
در واقع برای html parsing نیازی به mshtml نیست و میتونیم با خود javascript این کار رو انجام بدیم (بنظرم بهتر هم هست):

Imports System.Text
Imports Microsoft.Web.WebView2

Private Async Sub WV2_NavigationCompleted(
sender As Object,
e As Core.CoreWebView2NavigationCompletedEventArgs
) Handles WV2.NavigationCompleted

Dim script As New StringBuilder
script.AppendLine("function getImages() {")
script.AppendLine("var images =[];")
script.AppendLine("var links = document.querySelectorAll(""link[rel*='icon'] , link[rel='image_src']"");")
script.AppendLine("links.forEach(function(link) {images.push(link.href);});")
script.AppendLine("var metas = document.querySelectorAll(""meta[property='og:image'] , meta[name='msapplication-TileImage']"");")
script.AppendLine("metas.forEach(function(meta) {images.push(meta.content);});")
script.AppendLine("return images.toString();}")
script.AppendLine("getImages();")

Try
Dim result As String = Await WV2.ExecuteScriptAsync(script.ToString)
result = CStr(Json.JsonSerializer.Deserialize(result, GetType(String)))
Dim image_addresses = result.Split(","c)
For i As Integer = 0 To image_addresses.Length - 1
DownloadFile(image_addresses(i), Site_Uri)
Next

Navigation_Completed = True

Catch ex As Exception
Throw New Exception(ex.Message)
End Try
End Sub

بقیه کدها با پست 8 یکسان هست.

mazoolagh
جمعه 31 شهریور 1402, 20:32 عصر
چند نمونه :

barnamenevis
154945

stackoverflow
154946

microsoft
154947

mozilla
154948

IEEE
154949

mazoolagh
جمعه 31 شهریور 1402, 21:10 عصر
در این چند پست آخر بیشتر هدف روی اشنایی با روش کار با webview2 و queryselector متمرکز بود تا پاسخ مستقیم به پرسش اصلی.

برای دانلود favicon مستقیما میتونیم از خود webview2 کمک بگیریم:

Private Sub WV2_NavigationCompleted(
sender As Object,
e As Core.CoreWebView2NavigationCompletedEventArgs
) Handles WV2.NavigationCompleted
Try
DownloadFile(WV2.CoreWebView2.FaviconUri.ToString)
Catch ex As Exception
Throw New Exception(ex.Message)
End Try
Navigation_Completed = True
End Sub

ROSTAM2
شنبه 01 مهر 1402, 16:58 عصر
سلام و خسته نباشید...

این
Microsoft.Web.WebView2 رو توی ویژوال استودیو 2019 پیدا نکردم البته WebViewControl مربوط به Web.UI بود اما در پروژه های Windows Forms به این سادگی قابل استفاده نیست

mazoolagh
شنبه 01 مهر 1402, 18:51 عصر
سلام و روز خوش

بهتره با nuget manager بگیرین.

154954