Excel VBA Course
Excel VBA Course - From Beginner to Expert

200+ Video Lessons
50+ Hours of Video
200+ Excel Guides

Become a master of VBA and Macros in Excel and learn how to automate all of your tasks in Excel with this online course. (No VBA experience required.)

View Course

Run Macro when cell changes

0

Hi guys, I am solving the following issue: I want to scrape a title of website when link inserted in column A and put this value to relevant cell (next to it) in column B. The issue seems to be that once I paste the website in column A, the code reruns the entire list from column A2 to "last row" as defined in the code. Is there any way to only modify column B once a single column A is modified? I.e. if Ipaste a link in column A36 I get a title in B36, regardless of whether the cell is in the middle of the used range or at the very bottom. I would like to use this without having to re-run multiple inputs as it currently stands; (i.e. the loop "for i =2 to last row")? Also, I would like to change the below from Modular macro i.e. sub to private sub reacting to change (i.e. intersect function) where the 'target' is any cell from A:A range. Many thanks!


'''''Sub get_title_header()

Dim wb As Object
Dim doc As Object
Dim sURL As String
Dim lastrow As Long
lastrow = Sheet1.Cells(Rows.Count, "A").End(xlUp).row

For i = 2 To lastrow
Set wb = CreateObject("internetExplorer.Application")
sURL = Cells(i, 1)

wb.navigate sURL
wb.Visible = False

While wb.Busy
DoEvents
Wend

''HTML document
Set doc = wb.document

Cells(i, 2) = doc.Title

On Error GoTo err_clear
Cells(i, 3) = doc.GetElementsByTagName("h1")(0).innerText
err_clear:
If Err <> 0 Then
Err.Clear
Resume Next
End If
wb.Quit
Range(Cells(i, 1), Cells(i, 3)).Columns.AutoFit
Next i

End Sub'''''




Thank YOU!

Answer
Discuss

Discussion

Hello,
Thank you for your response - I perhaps did not explain this clearly, but the only issue with the code is that it re-runs through the whole column of records/http as opose to only those that were added or changed. 
In practice, this means that if there are websites populated within A2:A36 and I edit A35 only, its will still go through the whole list and reload A2:A36. Should the lkist be long, this will cause a significant delay and loss efficiency as there is no need to update records that are correctly populated - not added or edited.

Is there any way for the code to only affect those cells that changed as opposed to running through the whole range one by one?

Many thanks,

J
JayKay (rep: 2) Apr 8, '20 at 8:26 pm
Hello,
I perhaps did not explain this clearly, but the only issue with the code is that it re-runs through the whole column of records/http as opose to only those that were added or changed. 
In practice, this means that if there are websites populated within A2:A36 and I edit A35 only, its will still go through the whole list and reload A2:A36. Should the lkist be long, this will cause a significant delay and loss efficiency as there is no need to update records that are correctly populated - not added or edited.

Is there any way for the code to only affect those cells that changed as opposed to running through the whole range one by one?

Many thanks,

J
JayKay (rep: 2) Apr 8, '20 at 8:27 pm
Add to Discussion

Answers

0
Selected Answer

The code below does do what you want - sort of. Please try it and you will know what I mean. In essence, you shouldn't expect there to be an element "h1" in every one of your listed websites. In my trials there wasn't even a "Title". But the code loops over all the URLs in column A, writes what it find for a title in column B and ignores the error caused by the request for element "h1".

Option Explicit

Sub Get_Title_Header()

    ' Declaring and object as 'Object' instead of its proper type
    ' disables Intellisense dropdowns.
    ' Seasoned programmers, sometimes want "late binding" for reasons
    ' of compatibility, meaning they intend to use their program across
    ' different versions of Excel, which is exactly the reason why
    ' ready code copied from the Internet invariably use late binding.
    ' If your program is intended for wide distribution use early binding
    ' (proper object declaration) during programming and change to
    ' late binding (declare objects "As Object") only before publishing.
    ' Set the references mentioned below at Tools > References
    
    Dim IE As InternetExplorer  'Object = "Microsoft Internet Controls"
    Dim Doc As HtmlDocument     'Object = "Microsoft HTML Object Library"
    Dim sURL As String
    Dim Rng As Range
    Dim lastRow As Long
    Dim Rstart As Long, Rend As Long
    Dim Default As String
    Dim R As Long
    
    sURL = "Select the URL or contiguous URLs you wish to update," & vbCr & _
           "enter a cell or contiguous range address of such URLs," & vbCr & _
           "or confirm ""A1"" to update all."
    Default = "A1"
    lastRow = Sheet1.Cells(Sheet1.Rows.Count, "A").End(xlUp).Row
    Rstart = 2                          ' first row to process
    
    Do
        On Error Resume Next
        Set Rng = Application.InputBox(sURL, "Select URL to update", Default, Type:=8)
        If Err Then Exit Sub                ' user pressed Cancel
        
        If Not Application.Intersect(Rng, Sheet1.Cells(1, 1) _
                                    .Resize(lastRow - Rstart + 1, 2)) _
                                     Is Nothing Then Exit Do
            MsgBox "The range you specified isn't valid for the" & vbCr & _
                   "purpose of this macro. Please try again.", _
                   vbInformation, "Invalid range definition"
            Default = Rng.Address(0, 0)
    Loop
    
    With Rng
        If .Address(0, 0) = "A1" Then
            Rend = lastRow
        Else
            Rstart = Application.Max(.Row, Rstart)
            Rend = Application.Min(.Row + .Cells.Count - 1, lastRow)
        End If
    End With
    
    Set IE = CreateObject("InternetExplorer.Application")

    For R = Rstart To Rend
        sURL = Cells(R, "A").Value    ' the address is in C1
        Application.StatusBar = "Processing " & sURL

        IE.navigate sURL
        IE.Visible = False

        While IE.Busy
            DoEvents
        Wend

        ''HTML document
        Set Doc = IE.document
        Cells(R, 2).Value = Doc.Title

        On Error Resume Next
        Cells(R, 3).Value = Doc.GetElementsByTagName("h1")(0).innerText
    Next R

    IE.Quit
    Range("A:C").Columns.AutoFit
    Application.StatusBar = "All done"
End Sub

Edit 09 Apr 2020   ===================================

The code originally posted here was modified to ask the user to specify a cell or range of cell in columns A or B to be updated. If the macro is called from the worksheet (Developer tab > Macros -- [Select] > Run) the user can select a cell or range. Otherwise it can be entered.

If the user specifies "A1" the macro will update all items in the list, same as before. I have corrected referencing errors in the previous code and added progress information to show in the Status Bar (bottom left).

Discuss

Discussion

Hello,
Thank you for your response - I perhaps did not explain this clearly, but the only issue with the code is that it re-runs through the whole column of records/http as opose to only those that were added or changed. 
In practice, this means that if there are websites populated within A2:A36 and I edit A35 only, its will still go through the whole list and reload A2:A36. Should the lkist be long, this will cause a significant delay and loss efficiency as there is no need to update records that are correctly populated - not added or edited.

Is there any way for the code to only affect those cells that changed as opposed to running through the whole range one by one?

Many thanks,

J
JayKay (rep: 2) Apr 8, '20 at 8:27 pm
Add to Discussion


Answer the Question

You must create an account to use the forum. Create an Account or Login